Lucene源码阅读 IndexWriter结构

索引访问原则:

  1. 同一时刻,Lucene仅允许一个进程对其进行加入文档、删除文档、更新索引等操作;
  2. 同一时刻,Lucene允许多个线程同时对其进行检索。

索引的层次结构:

  1. 索引(Index):对于FSDirectory创建的索引库来说,指定索引生成目录后,目录下生成的所有文件构成一个索引;
  2. 段(Segment):索引包含多个段,不同的段由不同的写入线程构成,段与段之间相互独立,可以根据合并策略合并,其中,segments.gen和segments_5是段的元数据文件,保存了段的属性信息。
  3. 文档(Document):段包含多个文档,是编写代码控制的新建索引的基本单位,相当于MySQL表中的一条数据。
  4. 域(FIeld):一个文档包含多个域,域相当于MySQL表中的列名。
  5. 词(Term):词是索引的最小单位,是经过词法分析和语言处理后的字符串。

IndexWriter

try {
	Directory dir = new FSDirectory(Path.get("D:\\123"));
	IndexWriterConfig config = new IndexWriterConfig(analyzer);
	IndexWriter writer = new IndexWriter(dir, config);
	Document document = new Document();
	Field titleField = new Field("name", "name", TextField.TYPE_STORED);
	document.add(titleField);
	try {
		writer.addDocument(document);
	} catch (IOException e) {
		log.error("索引创建失败", e);
	}
	writer.close();
}
catch (Exception e) {
	LOGGER.error("索引构建失败", e);
}

IndexWriter对象:IndexWriter主要负责对索引的写入和索引的整体的维护,如合并,优化等操作;它由IndexWriterConfig和Directory两个参数构造而成。

  • IndexWriterConfig对象:IndexWriterConfig类似于Mybatis中的Configuration文件,主要涵盖了索引操作所需的各种操作策略及索引管理方法等。
  • Directory对象:索引文件的集合,用于管理索引文件,包括数据的读写,索引的添加删除等,提供了多种实现以支持在不同的存储结构中存储数据。

IndexWriterConfig对象属性详解:

  1. OpenMode:索引创建模式,包含CREATE(新建)、APPEND(追加)、CREATE_OR_APPEND(目录下没有则新建,否则追加)三种模式;
    • 采用APPEND模式如果当前目录下没有索引库则会出现 no segments* file found in LockValidatingDirectoryWrapper 异常;
    • 如果索引库由于某些特殊情况导致索引库异常,可以通过CREATE重建索引库;
  2. IndexDeletionPolicy:用于管理CommitPoint(每次Commit之后都会生成一个CommitPoint,用于索引库的回滚);
    • KeepOnlyLastCommitDeletionPolicy:仅保留最后一次Commit后产生的CommitPoint;
    • NoDeletionPolicy:保留所有的CommitPoint;
    • SnapshotDeletionPolicy:封装了其他的索引删除策略,可以通过snapshot()方法保留提交的快照,通过release()方法释放已保存快照,快照保存在内存中;
    • PersistentSnapshotDeletionPolicy:与SnapshotDeletionPolicy类似,区别在于这种方式保留的快照会持久化到硬盘上,通过保留segment_N的方式实现; writer.commit(); persistentSnapshotDeletionPolicy.snapshot();
  3. IndexCommit:IndexWriter允许从某一个提交点打开索引库,但是在CREATE模式下不允许setIndexCommit,会导致报错。
    • IndexCommit索引的变更如果要可见(如IndexDeletionPolicy,IndexReader中),必须提交COMMIT。每次提交都有一个唯一的segments_N文件与之关联。默认NULL。
  4. Similarity:Lucene核心的相关性算法,在数据写入和搜索时都会执行相关性算法,Lucene默认实现了TF-IDF和BM25算法。
    • 数据写入时会计算数据的标准化因子(Normalization Factor)并写入索引,标准化因子会影响查询时的打分计算,标准化因子的计算公式为 Document Boost(文档重要性,越高越重要) * Field Boost(域越大,越重要) * LengthNorm(Field)(域中包含的Term越少,文档越短,越重要) * π
    • 数据读取时主要根据Term的出现次数及Term的普遍程度,再乘以用于区分Term重要性的标准化因子,得出最终分数并排序
  5. MergePolicy:Index内多种行为可能触发Merge,比如Commit、Flush、NRT reader open。Lucene内部提供LogMergePolicy、TieredMergePolicy(默认)等策略。
    • LogMergePolicy:一种定长的合并方式,通过maxLevel、LEVEL_LOG_SPAN、levelBottom参数将连续的段分为不同的层级,再通过mergeFactor从每个层级中选取段进行合并。levelBottom(从左开始,大于这个值的段即每层结束位置) = maxLevel(最大段值) - LEVEL_LOG_SPAN(常量)
      • LogByteSizeMergePolicy:标明了maxMergeSize,超过这个段值则不会被合并;minMergeSize,用于处理大量的小段,将小于这个值的段划分为一层
      • LogDocMergePolicy:与LogByteSizeMergePolicy的区别在于不是通过文件大小,而是通过文档数量判断段的大小
    • TieredMergePolicy:与LogMergePolicy的区别在于会先对IndexWriter提供的段集进行排序,然后在排序后的段集中选取部分(可能不连续)段来生成一个待合并段集,即非相邻的段文件(Non-adjacent Segment)。
    • UpgradeIndexMergePolicy:用于老版本段与新版本之间的兼容合并。
  6. MergeScheduler:它用于执行一个或多个段的合并,并且提供了对Merge过程定制管理的能力。它也包含了多种合并策略以应对不同的情况。
    • NoMergePolicy:在这种策略下,即使你定义了合并策略,也不会执行段合并。
    • SerialMergeScheduler:多个线程使用同一个IndexWriter对象来生成索引时,当他们分别执行flush、commit操作后,就各自从合并策略中得到各自的OneMerge,这些OneMerge构成的集合就是MergeSpecification,接着MergeSpecification中的OneMerge被添加到pendingMerges中,pendingMerges是一个有序列表,IndexWriter通过synchronized关键字有序的将所有线程中获得的OneMerge添加到pendingMerges链表中,执行合并操作时,便从pendingMerges链表中取出OneMerge顺序合并,由于合并方法包含Synchronized,不允许多线程合并操作
    • ConcurrentMergeScheduler:使用这种执行策略,可以并发执行合并操作,且对象会通过一些参数限制最大合并线程数量、合并线程的I/O节流等。
  7. Codec:编码或解码一个倒排索引段,用于生成一个新的段。
  8. IndexerThreadPool:管理IndexWriter内部索引线程(DocumentsWriterPerThread)池,为使用同一个IndexWriter对象的并发操作提供多线程支持。
  9. ReaderPooling:实例化IndexReader是非常昂贵的操作,且它是一个线程安全的,跟索引目录是一一对应的,最好的方式就是用一个Pool去维护这些IndexReader:保证一个文件目录只有一个实例,且不同的IndexReader可以动态的组合。默认为false 不使用Pool。
  10. FlushPolicy:决定了In-memory buffer何时被flush,默认的实现会根据RAM大小和文档个数来判断Flush的时机,FlushPolicy会在每次文档add/update/delete时调用判定。
  11. RAMPerThreadHardLimitMB:设置每个线程最大的内存用量,超出则会自动执行flush操作。
  12. MaxBufferedDoc:Lucene提供的默认FlushPolicy的实现FlushByRamOrCountsPolicy中允许DocumentsWriterPerThread使用的最大文档数上限,超过则触发Flush。
  13. RAMBufferSizeMB:Lucene提供的默认FlushPolicy的实现FlushByRamOrCountsPolicy中允许DocumentsWriterPerThread使用的最大内存上限,超过则触发flush。
  14. Analyzer:Lucene分词器。
  15. MergedSegmentWarmer:预热合并后的新段,使其元数据能提前被Reader读取。
  16. InfoStream:用于调试信息的管理,默认为InfoStream.getDefault()不记录任何调试信息。

Directory对象

  1. RAMDirectory:RAMDirectory是内存中的一个区域,只需要简单的使用构造函数就可以得到其实例。
  2. FSDirectory:通过NativeFSLockFactory(本地文件锁工厂)进行锁的实现的一个本地索引库。在诸如Linux, MacOSX, Solaris和windows 64位操作系统的JRE中,使用MMapDirectory方式,在其他非windows操作系统的JRE中,使用NIOFSDirectory方式,其他windows操作系统的JRE中,使用SimpleFSDirectory方式。
    • SimpleFSDirectory:FSDirectory的简单实现,并发能力有限,遇到多线程读同一个文件时会遇到瓶颈。
    • NIOFSDirectory:通过java.nio’s FileChannel实行定位读取,支持多线程读(默认情况下是线程安全的)。该类仅使用FileChannel进行读操作,写操作则是通过FSIndexOutput实现。 注意:NIOFSDirectory 不适用于Windows系统,另外如果一个访问该类的线程,在IO阻塞时被interrupt或cancel,将会导致底层的文件描述符被关闭,后续的线程再次访问NIOFSDirectory时将会出现ClosedChannelException异常,此种情况应用SimpleFSDirectory代替。
    • MMapDirectory:通过内存映射进行读,通过FSIndexOutput进行写的FSDirectory实现类。使用该类时要保证用足够的虚拟地址空间。另外当通过IndexInput的close方法进行关闭时并不会立即关闭底层的文件句柄,只有GC进行资源回收时才会关闭。

Directory其他相关类:

  1. FileSwitchDirectory:文件切换的Directory实现.针对lucene的不同的索引文件使用不同的Directory .借助FileSwitchDirectory整合不同的Directory实现类的优点于一身。
  2. RateLimitedDirectoryWrapper:通过IOContext来限制读写速率的Directory封装类。
  3. CompoundFileDirectory:用于访问一个组合的数据流。仅适用于读操作。对于同一段内扩展名不同但文件名相同的所有文件合并到一个统一的.cfs文件和一个对应的.cfe文件内。
  4. TrackingDirectoryWrapper:Directory的代理类。用于记录哪些文件被写入和删除。

Lucene通过DocumentWriterPerThread的方式支持多线程写入,每个DocumentWriterPerThread单独写一个Segment; Segment在Commit、Flush、NRT reopen时都可能触发Merge操作,Merge是Segment的整理操作。

IndexWriter采用了文件锁,IndexWriter对象生成时会在索引目录下生成writer.lock文件,索引下包含这个文件即无法再创建IndexWriter对象否则会出现如下异常,直到现有的IndexWriter对象被close掉。

你可能感兴趣的:(数据库,python,java,mysql,大数据)