字段数据
字段数据(fielddata),在 Lucene 中又叫 uninverted index。我们都知道,搜索引擎会使用倒排索引(inverted index)来映射单词到文档的 ID 号。而同时,为了提供对文档内容的聚合,Lucene 还可以在运行时将每个字段的单词以字典序排成另一个 uninverted index,可以大大加速计算性能。
作为一个加速性能的方式,fielddata 当然是被全部加载在内存的时候最为有效。这也是 ES 默认的运行设置。但是,内存是有限的,所以 ES 同时也需要提供对 fielddata 内存的限额方式:
- indices.fielddata.cache.size
节点用于 fielddata 的最大内存,如果 fielddata 达到该阈值,就会把旧数据交换出去。该参数可以设置百分比或者绝对值。默认设置是不限制,所以强烈建议设置该值,比如
10%
。 - indices.fielddata.cache.expire 进入 fielddata 内存中的数据多久自动过期。注意,因为 ES 的 fielddata 本身是一种数据结构,而不是简单的缓存,所以过期删除 fielddata 是一个非常消耗资源的操作。ES 官方在文档中特意说明,这个参数绝对绝对不要设置!
Circuit Breaker
Elasticsearch 在 total,fielddata,request 三个层面上都设计有 circuit breaker 以保护进程不至于发生 OOM 事件。在 fielddata 层面,其设置为:
- indices.breaker.fielddata.limit
默认是 JVM 堆内存大小的 60%。注意,为了让设置正常发挥作用,如果之前设置过
indices.fielddata.cache.size
的,一定要确保indices.breaker.fielddata.limit
的值大于indices.fielddata.cache.size
的值。否则的话,fielddata 大小一到 limit 阈值就报错,就永远道不了 size 阈值,无法触发对旧数据的交换任务了。
doc values
但是相比较集群庞大的数据量,内存本身是远远不够的。为了解决这个问题,ES 引入了另一个特性,可以对精确索引的字段,指定 fielddata 的存储方式。这个配置项叫:doc_values
。
所谓 doc_values
,其实就是在 ES 将数据写入索引的时候,提前生成好 fielddata 内容,并记录到磁盘上。因为 fielddata 数据是顺序读写的,所以即使在磁盘上,通过文件系统层的缓存,也可以获得相当不错的性能。
注意:因为 doc_values
是在数据写入时即生成内容,所以,它只能应用在精准索引的字段上,因为索引进程没法知道后续会有什么分词器生成的结果。
由于在 Elastic Stack 场景中,doc_values
的使用极其频繁,到 Elasticsearch 5.0 以后,这两者的区别被彻底强化成两个不同字段类型:text
和 keyword
。
"myfieldname": {
"type": "text"
}
等同于过去的:
"myfieldname": {
"type": "string",
"fielddata": false
}
而
"myfieldname": {
"type": "keyword"
}
等同于过去的:
"myfieldname": {
"type": "string",
"index": "not_analyzed",
"doc_values": true
}
也就是说,以后的用户,已经不太需要在意 fielddata 的问题了。不过依然有少数情况,你会需要对分词字段做聚合统计的话,你可以在自己接受范围内,开启这个特性:
{
"mappings": {
"my_type": {
"properties": {
"message": {
"type": "text",
"fielddata": true,
"fielddata_frequency_filter": {
"min": 0.1,
"max": 1.0,
"min_segment_size": 500
}
}
}
}
}
}
你可以看到在上面加了一段 fielddata_frequency_filter
配置,这个配置是 segment 级别的。上面示例的意思是:只有这个 segment 里的文档数量超过 500 个,而且含有该字段的文档数量占该 segment 里的文档数量比例超过 10% 时,才加载这个 segment 的 fielddata。
下面是一个可能有用的对分词字段做聚合的示例:
curl -XPOST 'http://localhost:9200/logstash-2016.07.18/logs/_search?pretty&terminate_after=10000&size=0' -d '
{
"aggs": {
"group": {
"terms": {
"field": "punct"
},
"aggs": {
"keyword": {
"significant_terms": {
"size": 2,
"field": "message"
},
"aggs": {
"hit": {
"top_hits": {
"_source": {
"include": [ "message" ]
},
"size":1
}
}
}
}
}
}
}
}'
这个示例可以对经过了 logstash-filter-punct
插件处理的数据,获取每种 punct 类型日志的关键词和对应的代表性日志原文。其效果类似 Splunk 的事件模式功能: