场景
在elasticsearch中我们通常有查询、聚合、排序的需求,而在es中默认使用相关度评分来排序,但是我们也可以指定字段排序。
类似于MySQL中的select * from test where name=“张三” order by id;
在ES-8.x版本中通常我们只会创建需要的字段,而_id做为元字段会自动生成。
但是如果使用该_id排序时会报错(如下)
{
“content”: “{“error”:{“root_cause”:[{“type”:“illegal_argument_exception”,“reason”:“Fielddata access on the _id field is disallowed, you can re-enable it by updating the dynamic cluster setting: indices.id_field_data.enabled”}],“type”:“search_phase_execution_exception”,“reason”:“all shards failed”,“phase”:“query”,“grouped”:true,“failed_shards”:[{“shard”:0,“index”:“dtk_follow”,“node”:“gUZyKR3ERwKlPhLSCOPWcw”,“reason”:{“type”:“illegal_argument_exception”,“reason”:“Fielddata access on the _id field is disallowed, you can re-enable it by updating the dynamic cluster setting: indices.id_field_data.enabled”}}],“caused_by”:{“type”:“illegal_argument_exception”,“reason”:“Fielddata access on the _id field is disallowed, you can re-enable it by updating the dynamic cluster setting: indices.id_field_data.enabled”,“caused_by”:{“type”:“illegal_argument_exception”,“reason”:“Fielddata access on the _id field is disallowed, you can re-enable it by updating the dynamic cluster setting: indices.id_field_data.enabled”}}},“status”:400}”,
“status”: “400 Bad Request”,
“status_code”: 400
}
根据提示解决办法很简单,通过设置参数(id_field_data.enabled)就可以让_id排序
那么为什么默认_id是不允许排序的呢?让我们一起来分析下
ElasticSearch中_id分析
首先从_id本身来看
- _id字段是一个物理字段,用于唯一标识每个文档。而在ES中 默认的_id 并不会生成 Doc Values。
- 在ES中,如果字段没有使用Doc Values的数据结构来存储,那么该字段就不允许被排序或聚合。
那么Doc Values又是什么呢
- 在ES中,Doc Values 就是一种列式存储结构,默认情况下,在索引创建一个文档的字段时(非text属性),也会创建对应的Doc Values。
- Doc Values其实就是我们所说的正排索引,基于段生成且不可变,同时是序列化到磁盘的。
这里还有一个概念需要介绍一下:
因为文档值被序列化到磁盘,所以我们可以充分利用操作系统的内存。当 working set 远小于节点的可用内存,系统会所有的Doc Values值保存在操作系统的内存中,使得其读写十分高速; 当其远大于可用内存,操作系统会根据需要从磁盘读取Doc Values然后选择性的放到分页缓存中。
为了完整的理解这个问题,我们先引入下一个概念
正排索引和倒排索引
正排索引:就是我们刚刚说的Doc Values,在ES中主要用于排序、聚合等,默认是不分词,不支持text。
倒排索引:就是ES中最大的特点,由倒排表、词项字典、词项索引组成,全文检索非常高效。
通过一张图来看下两者的区别
现在看就很明显了,由于数据结构的不同,使用正排索引来排序将是非常高效的。而使用倒排索引排序就相当于全表扫描了。所以在ES中,默认禁止非Doc Values存储的数据排序或聚合。
与Doc Values相关联的概念还有fielddata
ES中fielddata
fielddata也是ES中的一种数据结构,与Doc Values不同,它由JVM管理,常驻于JVM Heap中。由于filedData是存储在内存中,所以一些高基数字段在生成 filedData 是会消耗大量的内存
其实fielddata和Doc Values的主要区别是:
- Doc Values是落盘的正排索引,需要排序或聚合时加载到内存就行了
- fielddata是对非正排索引需要排序或聚合时在内存中建立正排索引,fielddata 的构建和管理发生在 JVM Heap中
- 而且fielddata是常驻内存的,JVM垃圾回收并不会回收此区域的内存,官方解释如下
结论
现在就很通透了。
_id是ES的默认字段,并且非Doc Values属性,所以默认是不允许被排序或聚合的。
但是ES也专门对_id设置了参数:indices.id_field_data.enabled,设置为true就可以使用_id排序
而针对索引内自己创建的字段,要不就在创建索引的时候开启fielddata(如下)
要不就单独开启(如下)