lucene的segment与LSM的关联

what:

  LSM:Log Structured Merge Trees,日志结构合并树。

  LSM被设计来提供比传统的B+树或者ISAM更好写操作吞吐量,通过消去随机的本地更新操作来达到这个目标(根本原因:磁盘随机操作顺序读写,快大约3个数量级。关联文章:https://www.cnblogs.com/sfzlstudy/p/15852383.html)。

 

  Segment:Lucene的一个Index会由一个或sub-index构成。sub-index被称为Segment,每个segment中包含documents文件,一个segment中会有完整正向索引和反向索引。

    搜索时,segments基本单位独立搜索每个segments文件,而后再把搜索结果合并

 

why:

  LSM出现的理由

    顺序写入几乎能够达到磁盘的理论值“200~300 MB/s”(例如:数据库的WAL(write-ahead log) ),但是的时候就会非常困难

    为了提高读的效率,强加结构信息于数据上,数据被按照特定的方式放置,结果就会:对写操作不友善,让写操作性能下降。

    常有用的4种方式:

      1、二分查找: 将文件数据有序保存,使用二分查找来完成特定key的查找;

      2、哈希:用哈希将数据分割为不同的bucket;

      3、B+树:使用B+树 或者 ISAM 等方法,可以减少外部文件的读取;

      4、外部文件: 将数据保存为日志,并创建一个hash或者查找树映射相应的文件;

 

    LSM 使用一种不同于上述四种的方法,保持了数据写性能,以及微小的读操作性能损失。总体是:操作顺序化

 

  Segment出现理由

    1、简化文档的逻辑解耦文档和文档。若无,则要修改整个索引,所以会影响到文档的读。

    2、提升文档的速度。只是创建包含单个文档的Segment,所以速度比较快;并且段里的数据都是排序好的,所以在和已有段合并的时候速度也是比较快的

 

how:

  LSM的核心思想

 

 

     LSM树有以下三个重要组成部分:

      1、MemTable:存在于内存中,用于保存最近更新的数据,按Key有序地组织。断电丢失数据,因此通常会通过WAL(Write-ahead logging,预写式日志)的方式来保证数据的可靠性

      2、Immutable MemTable:存在于内存中,当 MemTable达到一定大小后就会变成Immutable MemTable。可理解为不可写的MemTable,是MemTable到SSTable的中间态。

      3、SSTable(Sorted String Table):有序键值对集合,是LSM树在磁盘中的数据结构。为了加快SSTable的读取,可以通过建立key的索引以及布隆过滤器来加快key的查找(如下图)。

 

 

        LSM树会将所有的数据插入、修改、删除等操作记录保存在内存之中,当此类操作达到一定的数据量后,再批量地顺序写入到磁盘当中(B+树数据的更新,会直接数据所在处修改对应的值;LSM数的数据更新,是日志直接顺序append)。 

        不同的SSTable中,可能存在相同Key的记录,当然最新的那条记录才是准确的。可能存在2个问题:

          a、Key冗余存储。除了最新的那条记录外,其他的记录都是冗余无用的。需要进行Compact操作(合并多个SSTable)来清除冗余的记录。

          b、读取时需要从最新的倒着查询,直到找到某个key的记录。会出现:不存在的key,需要将所有的SSTable找完(所以索引/布隆过滤器来优化)。

 

 

  LSM树的Compact策略

    Compact操作是十分关键的操作,否则SSTable数量会不断膨胀。在Compact策略上,主要介绍两种基本策略:size-tiered和leveled

    1、读放大。实际读取的数据量大于真正的数据量。例如:在LSM树中需要先在MemTable查看当前key是否存在,不存在继续从SSTable中寻找。

    2、写放大。实际写入的数据量大于真正的数据量。例如:在LSM树中写入时可能触发Compact操作,导致实际写入的数据量远大于该key的数据量。

    3、空间放大。实际占用的磁盘空间比数据的真正大小更多(原因是:数据冗余)。

 

    size-tiered策略:

      每层SSTable的大小相近(不同层的SSTable大小不同),同时限制每层SSTable的数量相同

 

 

       例如:每层限制SSTable为N,当每层SSTable达到N后,则触发Compact操作合并这些SSTable,并将合并后的结果写入到下一层成为一个更大的sstable。会出现:最底层的单个SSTable的大小会变得非常大。对于同一层的SSTable,每个key的记录是可能存在多份的,只有当该层的SSTable执行compact操作才会消除这些key的冗余记录。

  

    leveled策略:

      采用分层的思想,每一层限制总文件大小,SSTable的大小相近(SSTable大小限制,和size-tiered策略相同)。但是同一层的key是全局有序的,并且唯一。

      合并策略和size-tiered不同,如下:

      1、L1的总大小超过L1本身大小限制:

 

       2、从L1中选择至少一个文件,然后把它跟L2有交集的部分(非常关键)进行合并。生成的文件会放在L2

 

         L1第二SSTable的key的范围覆盖了L2中前三个SSTable,那么就需要将L1中第二个SSTable与L2中前三个SSTable执行Compact操作。

      3、L2合并后的结果仍旧超出L5的阈值大小,需要重复之前的操作 —— 选至少一个文件然后把它合并到下一层:

 

         注意:不同层可以并发合并

      

  Segment

    继承了LSM(Log Structured Merge Trees)中数据写入的优点,但是在查询上只能提供近实时而非实时查询。

    Lucene中的数据写入会先写内存的一个Buffer(类似LSM的MemTable,但是不可读,是不可被搜索的),当Buffer内数据到一定量后会被flush成一个Segment,每个Segment有自己独立的索引,可独立被查询,但数据永远不能被更

    Index的查询,对个Segment进行查询并对结果进行合并,还需要处理被删除的文档。为了优化,Lucene会有策略对多个Segment进行合并(和LSM类似)。

 

posted @ 2022-05-05 18:20  修心而结网  阅读(395)  评论(0编辑  收藏  举报