Hello,World

关于ES的聚合的一些认识1

Elasticsearch | 作者 Charele | 发布于2024年03月20日 | 阅读数:2019

只说关于termAgg相关的东东,
termAgg是最基础的,明白了这个,可以更好的理解其它的。
 
各种类型的聚合,尽管结构上五花八门,
但是执行逻辑上是一样的(pipeline类型的除外) 
1111.PNG

如果你对一个比如name字段做term聚合,缺省用的就是这个类
(会有优化变种,不谈)
 
它以GlobalOrdinals开头,就说明这个GlobalOrdinals是灵魂,就说说它是怎么来的
 
就是说如果对姓名做term聚合,它并不是直接统计张三有几个,李四有几个,
而是用这个ordinal号代表每个姓名,对这个号进行统计的
已邀请:

Charele - Cisco4321

赞同来自: liaosy

ES里面的fieldData概念,体现在代码里,实际就是这个IndexFieldData(的子类)
 加载fieldData的流程流程如下:
Index级FD ---> 叶(或段)级FD ---> 产生每个段的FieldData ===> 形成全局FD
  其中红色子类就是带GlobalOrdinals号的,用来像term聚合。
 
那那些不带GlobalOrdinals号的,有啥用呢?也用来聚合的马?
1111.PNG

 
 
 

Charele - Cisco4321

赞同来自:

ordinal,简单说就是编号,比如name,张三是0,李四是1,王五是2,,,
GlobalOrdinals,就是全局的编号,你公司有张三是0号,而我公司张三是10号,这就不行了,得形成共识。
这个"Global",是相对局部的(Lucene)段来说的。
 
如果你的name字段是keyword类型的,它低层用的是docValue,每个词写的时候天生就带唯一编号,
直接用一个Lucene方法取出来就可以了。
1111.PNG

现在这个号只是每个段内部的,不是全局的,
比如你段,bbb:0, ccc:1, ddd:2,,,
我段:aaa:0, bbb:1,,, xxx:21
 
因为每个段拥的词都不一样,所以bbb,在每个段里的号不一样。
后面会说如何形成全局的号。

Charele - Cisco4321

赞同来自:

如果你的name字段是text类型的,它是不带编号的,
在做聚合的时候,它是在查询临时生成的
1111.PNG

绿色就是取出一个词,红色就是为这个词按序编一个号,0,1,2,3,,
 
紫色处,就是把这个词保存下来
配合黄色的记录的偏移量,到时候就可以很方便的取出词来。
蓝色处,会记录文档号。这几个,一个都不能少,过程比慢麻烦。
   因为text类型会分词,所以量比较大,在这个过程中会有熔断机制。

Charele - Cisco4321

赞同来自:

好了,每个段的ordinals已经解决了,下面说说如何形成全局ordinals
 
首先如果只有一个段,那就是全局的,因为没人和你争论什么
1111.PNG

 
如果有多个段,会在红色处生成一个全局的ordinals列表,
这个列表是个大杂烩,很大。
 
显然,如果我这个段很小,只有几个词,显然用不到这么大的一张表,
所以会为每个段形成一个“lazy加载”的形表,紫色处
 
当然,它直接返回这个大杂烩也是可以的,这么做只是优化性能
2222.PNG

Charele - Cisco4321

赞同来自:

现在有了一个全局的大杂烩,说说如何针对每个段生成自己的ordinals(代表词做统计)
因为大杂烩中有所有的词和编号,aaa:0, bbb:1, ccc:2,,,,
 
比如,如果我的段中只有一个词bbb,那我只要"bbb:1"这一段就行了
汪意:现在这个"bbb:1"是全局的,就是说在所有的段现在的视角来看,bbb的号都是1
(尽管你段里存的是bbb的编号是0)

1111.PNG

红色,就是说如果你段里的词数量和大杂烩里的词数量一样,
显然你就需要这个大表,直接返回。
 
绿色和紫色是针对你段中,name这个字段是单值和多值的情况返回不同的对像
 
 

Charele - Cisco4321

赞同来自:

1111.PNG

这是在term聚合中实际动作,
黄色,就表示这个文档里没有这个字段,不管它了。
 
红色就是得到这个文档中词的ord,绿色里面,实际上的就是这个ord的 count+1 处理
 
这里并不需要知道是什么词,ord就唯一代表了一个词。
最后返回结果时会找出ord对应的词,把<词,count>返回
最后多个分片的结果会做合并,就是最终的查询结果了
 
 
另外,这是单值的情况,
多值会不一样。因为多值情况下,一个文档里会有多个不同的词

Charele - Cisco4321

赞同来自:

如果只是单层聚合,这就可以了。
但如果有子聚合,还是不够的。
因为如果像"count by 公司,姓名“这样的聚合查询,
”张三“只有一个唯一的全局ord,
 
你公司有两个叫张三的,这是同一个
但你不能把你公司的张三和我公司的张三看成一样的
 所以在有子聚合的情况下,它会做remap。
1111.PNG

 
就是把这个桶号 + ord,形成一个新的叫"bucketOrd",来做统计
类似于:
a公司:张三 ---> 0
b公司:张三 ---> 1
a 公司:李四 ---> 2,,,,

Charele - Cisco4321

赞同来自:

在termAgg中,除了上面所说的这种globalOrdinal方式,
 
还有另外一种"map"方式的聚合方式,
 map方式中,并不是不需ord而直接对词进行统计,
它也需要ord,只是取得方式不同而已
2222.PNG

 
 

Charele - Cisco4321

赞同来自:

上面搞的这些GlobalOrdinals数据,ES里头叫做fieldData,
会进cache的。
 
就是说,找的时候先去cache看下,如果有就从cache直接取来。
没有就生成,然后放进cache以供后续查询读取
1111.PNG

 

Charele - Cisco4321

赞同来自:

1111.PNG

 
相关的,ES里面有个warm机制,预热,
 
下面那个:
如果你的字段设置有"eager_global_ordinals": true,
每次有新段生成时,它都会把这个字段的fieldData预先加载进缓存。
查询要用时就不必再生成了
 
 上面那个BitSetProducerWarmer,
是为Nested文档而作用的,
为什么要预热Nested文档,是为了加快Nested查询。
至于怎么个原理,要看Lucene代码才能明白。
 
 warm执行时用的是“WARMER”线程(这个线程池只用在这里) 
 
这两个,是针对不同的字段类型进行处理,
一个是预热"eager_global_ordinals"字段,一个是预热nested字段。
那如果我一个字段,既是nested,又是"eager_global_ordinals=true",会不会两头预热呢?
不会的,因为nested字段设置不了"eager_global_ordinals"这个属性。

要回复问题请先登录注册