lucene-新增,更新,删除过程解析

lucene索引的核心流程

核心流程:新增,修改,删除,查询。
删除操作:没有什么特别的,内存中使用一个list对象记住将被删除的数据,调用commit数据最终才会被删除。
修改操作:先执行删除后新增的过程。

索引的基本流程

  1. 提取文本和创建文档:构建document实例和Field实例的过程
  2. 分析文档:将文本数据分割成词汇单串(看用的是什么解析器,不用的解析器会对英文的大小写,停词,以及中文的分词方式会有不同的处理)
  3. 向索引中新增文档:分析文档后,将分析结构以倒排索引的数据结构进行存储

新增索引

代码用例

    @Test
    public void addDoc() throws Exception {
        dir  = FSDirectory.open(Paths.get("/Users/siminglang/Desktop/Demo5"));
        IndexWriter indexWriter = getIndexWriter();
        for (int i = 0; i < ids.length; i++) {
            Document doc = new Document();
            doc.add(new StringField("id", ids[i], Field.Store.YES));
            doc.add(new StringField("city", citys[i], Field.Store.YES));
            doc.add(new TextField("desc", descs[i], Field.Store.NO));
            indexWriter.addDocument(doc);
        }
        indexWriter.close();
    }

基本流程:创建一个indexWriter类, 构建document实例,调用addDocument方法新增文档,调用indexWriter.close(方法内部会调用commit方法)提交文档

lucene新增索引的过程有何特点

  • 写入索引过程中会触发的操作:flush,commit,merge
  • lucene索引文档前,会判断是否需要进行段合并操作。 为什么要这样做,已经如何去实现它。
lucene的flush操作,实现了什么能力。又是怎么实现的

实现的能力:将缓存中的数据以segment的形式写入到磁盘当中。写入磁盘后,缓存会被释放。
如何实现:构建segmeng对象,写入到磁盘中。
在这里插入图片描述
在这里插入图片描述

lucene的commit又实现了什么能力呢?
  • commit提供的能力:让所有的更改,包括缓存中的更改或者已经刷新的能改在索引中保持可见。
  • 由lucene源码可知,commit的时候会执行flushAllThreads方法,对某个indexWriter所有的线程执行flush操作。
lucene数据写入过程流程图

在这里插入图片描述

lucene的merge操作

什么是merge操作:
段合并,如果索引中包含太多的段,IndexWriter会选择其中一些段并将它们合并成一个唯一的更大的段
好处:
加快搜索速度,因为被搜索的段的数量更少了;减少来索引的尺寸,例如如果被合并的段中包含删除操作的话,那么合并过程会释放文档删除标识所占用的数据位。即使没有删除操作,一般来说合并后的段也会占用更少的存储空间。
段合并时机:
变更操作进行刷新时;或者上一个段合并操作已经完成时
合并策略
段合并策略LogByteSizeMergePolicy,IndexWriter默认的情况下使用。该策略会测量段包含的所有文件的总字节数。
段合并策略LogDocMergePolicy,它测量的是段中的文档数量。
会按照段的尺寸对段进行分组,处于同一分组的段才可能被合并。每次合并时,只会取一个分组中的某些段进行合并,不会合并一个分组中的所有段,会控制参与合并的段的数量。maxMergeMB或者maxMergeDocs可以控制超过改数值的段不参与段合并,它们是大段。
控制段合并的类
MergePolicy来控制段合并
实施合并操作的类:MergerScheduler。它有两个子类,一个是ConcurrentMergeScheduler,后台线程完成段合并; 一个是SerialMergeScheduler,调用它的线程完成段合并。

删除索引

代码用例

    @Test
    public void deleteDoc() throws Exception {
        dir  = FSDirectory.open(Paths.get("/Users/siminglang/Desktop/Demo5"));
        IndexWriter indexWriter = getIndexWriter();
        indexWriter.deleteDocuments(new Term("id", "1"));
        indexWriter.commit();
        System.out.println("indexWriter.hasDeletions() :" + JSON.toJSONString(indexWriter.hasDeletions() ));
        System.out.println("indexWriter.getInfoStream() :" + JSON.toJSONString(indexWriter.getInfoStream()));
        System.out.println("indexWriter.getDocStats() :" + JSON.toJSONString(indexWriter.getDocStats()));
    }

基本流程:创建一个indexwriter对象;构建一个Term删除条件,匹配条件对的文档将会被删除;调用commit提交删除操作;

特性

  • 调用commit或者是close可以向索引中提交删除操作
  • 及时删除操作完成,存储文档的磁盘也不会立即被释放,lucene只是将文档标记为删除。真正的删除操作是在文档那对应的段进行合并时才执行
  • lucene中实现文档删除能力的类有两个:IndexReader和IndexWriter。IndexReader立即决定删除哪个文档。IndexWriter仅仅是将被删除的Term进行缓存,后续再进行实际的删除操作。
  • lucene使用一个简单的方式记录被删除的文档,使用bit数组的形式记录,这个操作速度非常快,但是被删除的文档数据依然占用文档空间。

更新文档

lucene更新文档的过程,其实是将文档删除后新增的过程。特性和流程和上述的类似。

lucene新增文档过程

疑问:

  • Field.Store字段的含义是?Store.YES 保存 可以查询 可以打印内容 ; Store.NO 不保存 可以查询 不可打印内容 由于不保存内容所以节省空间
  • 完成写入过程的类?DefaultIndexingChain.StoredFieldsConsumer完成字段的写入过程。
  • lucene是如何控制写入速度的?通过flushController进行控制,执行插入动作前,需要保证排队中的flush个数和停顿的线程数为0, 否则将执行flush操作。
  • 整理下DocumentsWriterFlushControl的能力

什么是lucene中的segment

  • lucene的索引是由一个或者多个segment组成的
  • 每个segment都是一个独立的索引,它是整个文档索引的一个子集
  • 每当write刷新缓冲区新增的文档,以及挂起目录删除操作时,索引文件都会建立一个新的段
  • 在搜索索引时,每个段都是单独访问的,但搜索结果是合并后返回的
  • 为了防止segment过多,影响搜索效率,lucene有segment合并策略,会周期性的将一些满足条件的segment合并成较大的segment。

何时回收被删除文档使用的磁盘空间

  • 发生段合并的时候
  • 显式调用optimize的时候,操作所有的段
  • expungeDeletes 只针对进行了删除操作的段进行合并

缓存和刷新

  • 为什么要用缓冲技术:减少磁盘IO
  • 当新增一个文档到lucene索引时,或者挂起一个删除操作时,这些操作首先会被刷到内存当中,而不是立即刷到磁盘中
  • 什么时候会触发将缓存中的数据刷新的磁盘当中呢?
    • 当缓存所占用的空间超过预设的RAM比例时进行刷新,预设方法为setRAMBufferSizeMB
    • 指定文档号对应的文档添加进索引后,通过调用setMaxBufferedDocs完成刷新
    • 在删除项和查询语句等操作占用的缓存总量超过预设值,可以通过调用setMaxBufferedDeleteTerm触发刷新操作。
    • 默认情况下,indexWriter只有在RAM用量为16M时启动刷新操作

索引提交

  • 索引提交后的可视性问题?
  • indexWriter的提交步骤:
  • 两阶段提交
  • 索引删除策略
    • IndexDeletionPolicy负责通知IndexWriter核实能够安全的删除旧的提交
    • 默认的KeepOnlyLastCommitDeletionPolicy, 该策略在每次创建完新的提交后删除先前的提交

如何提升索引文档的吞吐量

  • 常用方式:使用固态磁盘SSD;升级到最新的lucene版本;升级到最新的java版本;;indexWriter,indexReader,indexSearch尽量使用共享实例;使用多线程;尽量给lucene配置更多的内存
  • 尽可能的减少段的合并操作,因为该操作会消耗大量的CPU和IO资源
  • 调整indexWriter刷新事件的阈值,调用方法setRAMBufferSizeMB。通常来说缓冲值是越大越好,但是你需要通过测试来得到一个合适的实践值。
  • 关闭复杂文件格式选项,IndexWriter.setUseCompoundFile(false)
  • 对Document实例和Filed实例进行重用。
  • 针对不同的mergeFactor值进行测试,更高的值来带更低的索引开销,但同时也意味着更低的索引速度。但是一定要在真实的环境中进行测试。

并发,线程安全及锁机制

要点梳理

  • 任意数量的indexReader都可以同时打开同一个索引。但是需要记住,利用资源和发挥效率最好的方法是利用多线程共享一个indexReader实例。
  • 对于一个索引来说,一次只能打开一个Writer。lucene采用文件锁的方式来提供保障。一旦建立起来indexWriter对象,系统会立即分配一个锁给它。该锁只有当IndexWriter对像关闭时才释放。
  • 注意如果使用indexReader来改变索引的话,这时indexReader对象会作为writer使用,它必须在执行修改动作之前获取到锁,并在关闭时会释放掉锁。
  • indexReader 可以在indexWriter正在修改索引的时候打开。
  • indexReader对象只有在indexWriter对象提交修改或者自己重新打开后才能获知索引的修改情况??一个更好的选择,打开indexReader时采用的参数create=true。这样的话,indexReader会持续检查索引的情况。
  • idnexWriter和indexReader都是线程安全且是线程友好的( 标记为synchronized的代码并不多,仅为最小值)
  • lucene采用的文件锁:如果锁文件(默认是write.lock)存在你的索引目录内,说明此时有一个writer打开了索引,如果针对同一索引创建一个新的writer的话,将产生一个lockObtainFailedException异常。这是一个重要保护机制,因为针对同一索引打开两个writer,会导致索引锁坏。
  • 参看有关刷新和段合并的有关信息:IndexWriterConfig conf = new IndexWriterConfig(analyzer);
    conf.setInfoStream(System.out);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值