1-Intro
基本的数据结构
- 倒排索引使用
MMAP - 对于
Keyword,long等非text在磁盘上有 列存,也就是docValues对 排序和聚合很友好,也可以在 关闭了倒排索引(index=false) 的时候进行查询,性能不如原始的倒排索引 fielddata强行给text开启 列存分析,成本太高,官方非常不建议- 高级数据结构,针对一些特殊的类型会使用 会在内存上开辟新的数据结构
- completion: 类似 Trie 树
- vector: 使用了
HNSW - Node query cache: 会使用
LRU为filter查询进行缓存.
2-Write 原理
Tips
Es的refreshApi会调用Lucene的flushAllThreads方法,而Es的flushApi调用了lucene的commit方法,可能会混淆
为什么写入 es 之后没有 refresh 的时候很可能看不见这个数据?
Es有一个IndexBuffer, 其实就是Lucene的IndexBuffer;Es的Shard对应一个Lucene实例, 这个实例有多个Segment;Es的indexdocument仅仅是调用了Lucene的Api-AddDocument, 写入到了IndexBuffer, 此时一般不可见,需要触发refresh机制,es的refresh触发了lucene源码中的flush- 可能的原因:
doc数目达到阈值DWPT的buffer达到一个配置的阈值, 默认是堆内存的10%, 达到上限了就会flush- 当前写入的
DWPT内存达到RAMPerThreadHardLimitMB限制,也很难发生
- 所以 要
es周期性或者手动调用refresh才能保证触发 Lucene当前的flush的操作不会阻塞当前的写入
- 可能的原因:
lucene 写入数据原理图如下:

哪怕是 refresh 依旧是 仅仅把 数据从 堆内存写入到 pageBuffer

refresh 之后会触发 mayBeMerge 尝试去触发 多个
segment的合并.
lucene的segmentmerge机制是size-based的, 把相同大小的合并到一起merge的操作是纯异步的- 太大的
segment将永远留着, 这也是不要轻易forceMerge的一个原因. - 算法:
- segments按size降序排列
- 计算
total segments size和minimum segment size total segments size过滤掉tooBigSegment(大于max_merged_segment/2.0)的segment,并记录tooBigCount;minSegmentBytes如果小于floor_segment(默认2mb),取2mb- 计算allowedSegCountInt,当segments(不包含tooBigSegment)数量大于此数,将触发merge
- 从大到小遍历段,以每个段为起点,使用贪心算法找出不大于 maxMergeAtOnce 且总大小不超过 maxMergedSegmentBytes 的段作为待合并对象。选择合并得分(mergeScore)最低的来进行合并
Lucene. 的 commit 太慢了,所以
Es设计了TransLog
- 要想彻底的持久化,需要调用
commit, 对性能影响太大. - 为了保证一致性,
Es做了特别的工作,就是TransLog来保证一致性,原理类似Mysql的RedoLog, 使用顺序 IO, Translog - 而
Es的flushApi则是调用lucene的commit并且生成一个新的TransLog