1-Intro

基本的数据结构

  • 倒排索引使用 MMAP
  • 对于 Keyword, long 等非 text 在磁盘上有 列存,也就是 docValues 对 排序和聚合很友好,也可以在 关闭了倒排索引(index=false) 的时候进行查询,性能不如原始的倒排索引
  • fielddata 强行给 text 开启 列存分析,成本太高,官方非常不建议
  • 高级数据结构,针对一些特殊的类型会使用 会在内存上开辟新的数据结构

2-Write 原理

Tips

EsrefreshApi 会调用 LuceneflushAllThreads 方法,而 EsflushApi 调用了 lucenecommit 方法,可能会混淆

为什么写入 es 之后没有 refresh 的时候很可能看不见这个数据?

  • Es 有一个IndexBuffer , 其实就是 LuceneIndexBuffer ;
  • EsShard 对应一个 Lucene 实例, 这个实例有多个 Segment ;
  • Esindex document 仅仅是调用了 LuceneApi-AddDocument, 写入到了 IndexBuffer, 此时一般不可见,需要触发 refresh 机制, esrefresh 触发了 lucene 源码中的 flush
    • 可能的原因:
      • doc 数目达到阈值
      • DWPTbuffer 达到一个配置的阈值, 默认是堆内存的 10%, 达到上限了就会 flush
      • 当前写入的 DWPT 内存达到 RAMPerThreadHardLimitMB 限制,也很难发生
    • 所以 要 es 周期性或者手动调用 refresh 才能保证触发
    • Lucene 当前的 flush 的操作不会阻塞当前的写入

lucene 写入数据原理图如下:

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

refresh 之后会触发 mayBeMerge 尝试去触发 多个 segment 的合并.

  • lucenesegment merge 机制是 size-based 的, 把相同大小的合并到一起
  • merge 的操作是纯异步的
  • 太大的 segment 将永远留着, 这也是不要轻易 forceMerge 的一个原因.
  • 算法:
    1. segments按size降序排列
    2. 计算 total segments sizeminimum segment size
    3. total segments size 过滤掉 tooBigSegment (大于max_merged_segment/2.0)的segment,并记录 tooBigCountminSegmentBytes 如果小于 floor_segment(默认2mb),取2mb
    4. 计算allowedSegCountInt,当segments(不包含tooBigSegment)数量大于此数,将触发merge
    5. 从大到小遍历段,以每个段为起点,使用贪心算法找出不大于 maxMergeAtOnce 且总大小不超过 maxMergedSegmentBytes 的段作为待合并对象。选择合并得分(mergeScore)最低的来进行合并

Lucene. 的 commit 太慢了,所以 Es 设计了 TransLog

  • 要想彻底的持久化,需要调用 commit, 对性能影响太大.
  • 为了保证一致性, Es 做了特别的工作,就是 TransLog 来保证一致性,原理类似 MysqlRedoLog, 使用顺序 IO, Translog
  • EsflushApi 则是调用 lucenecommit 并且生成一个新的 TransLog

3-分布式算法