Lucene隨筆-聊聊IndexWriter

Lucene版本:6.5.1
Package: org.apache.lucene.index;

IndexWriter示例

這裏以"hello world"的索引過程爲例,探究以下IndexWriter的原理:

  • doc1:索引文件。
  • path: 索引相關的文件所存放的文件夾位置。

IndexWriter的大致過程如下:

  1. 首先創建IndexWriter。
  2. 創建需要索引的文檔。
  3. 通過IndexWriter將文檔寫入。
  4. 提交以及關閉IndexWriter。
		String doc1 = "hello world";
		String path = "pathFile";
		
        // 創建IndexWriter
        Directory d = FSDirectory.open(Paths.get(PATH));
        IndexWriterConfig conf = new IndexWriterConfig(new SimpleAnalyzer());
        IndexWriter indexWriter = new IndexWriter(d, conf);

        // 把要創建的索引的文本數據放入Document中
        Document ducument1 = new Document();
        ducument1.add(new TextField("id", "1", Field.Store.YES));

        // 通過IndexWriter把Document寫入
        indexWriter.addDocument(ducument1);
		
		// 調用commit函數將數據組合成segment,這個時候數據才能被檢索
        indexWriter.commit();
        indexWriter.close();

整個查詢流程總結如下:

  • 初始化:初始化IndexWriter必要的兩個元素是Directory和IndexWriterConfig,Directory是Lucene中數據持久層的抽象接口,通過這層接口可以實現很多不同類型的數據持久層,例如本地文件系統、網絡文件系統、數據庫或者是分佈式文件系統。這裏lucene裏面說明了採用NFS的模式相對於本地文件系統會導致性能下降。
  • 構造文檔:Lucene中文檔由Document表示,Document由Field構成。
  • 寫入文檔:通過IndexWriter的addDocument函數寫入文檔,寫入時同時根據FieldType創建不同的索引。
  • 提交刷新文檔:當數據寫入後並不是立即搜索的,需要調用commit函數,這時候會手動出發一次flush才能將數據組織成segment實現可檢索。

IndexWriter創建

Class IndexWriterConfig

IndexWriterConfig裏面包含了IndexWriter的配置情況,包括索引以何種形式寫入、分析器的類型等等。
注意:用這個配置對象創建好IndexWriter對象後,再修改這個配置對象的配置信息不會對IndexWriter對象起作用。如要在indexWriter使用過程中修改它的配置信息,通過 indexWriter的getConfig()方法獲得 LiveIndexWriterConfig 對象,在這個對象中可查看該IndexWriter使用的配置信息,可進行少量的配置修改

其中部分的核心參數如下:

  • OpenMode: IndexWriter的打開方式,包含了三種模式(CREATE, APPEND, CREATE, CREATE_OR_APPEND), CREATE表示新建或者重寫一個index;APPEND表示打開一個存在的index;CREATE_APPEND表示
  • IndexDeletionPolicy:Lucene開放對commit point的管理,通過對commit point的管理可以實現例如snapshot等功能。Lucene默認配置的DeletionPolicy,只會保留最新的一個commit point。
  • Similarity:搜索的核心是相關性,Similarity是相關性算法的抽象接口,Lucene默認實現了TF-IDF和BM25算法。相關性計算在數據寫入和搜索時都會發生,數據寫入時的相關性計算稱爲Index-time boosting,計算Normalizaiton並寫入索引,搜索時的相關性計算稱爲query-time boosting。
  • MergePolicy:Lucene內部數據寫入會產生很多Segment,查詢時會對多個Segment查詢併合並結果。所以Segment的數量一定程度上會影響查詢的效率,所以需要對Segment進行合併,合併的過程就稱爲Merge,而何時觸發Merge由MergePolicy決定。
  • MergeScheduler:當MergePolicy觸發Merge後,執行Merge會由MergeScheduler來管理。Merge通常是比較耗CPU和IO的過程,MergeScheduler提供了對Merge過程定製管理的能力。
  • Codec:Codec可以說是Lucene中最核心的部分,定義了Lucene內部所有類型索引的Encoder和Decoder。Lucene在Config這一層將Codec配置化,主要目的是提供對不同版本數據的處理能力。對於Lucene用戶來說,這一層的定製需求通常較少,能玩Codec的通常都是頂級玩家了。
  • IndexerThreadPool:管理IndexWriter內部索引線程(DocumentsWriterPerThread)池,這也是Lucene內部定製資源管理的一部分。
  • FlushPolicy:FlushPolicy決定了In-memory buffer何時被flush,默認的實現會根據RAM大小(默認16mb)和文檔個數來判斷Flush的時機,FlushPolicy會在每次文檔add/update/delete時調用判定。
  • MaxBufferedDoc:Lucene提供的默認FlushPolicy的實現FlushByRamOrCountsPolicy中允許DocumentsWriterPerThread使用的最大文檔數上限,超過則觸發Flush。
  • RAMBufferSizeMB:Lucene提供的默認FlushPolicy的實現FlushByRamOrCountsPolicy中允許DocumentsWriterPerThread使用的最大內存上限,超過則觸發flush。
  • RAMPerThreadHardLimitMB:除了FlushPolicy能決定Flush外,Lucene還會有一個指標強制限制DocumentsWriterPerThread佔用的內存大小,當超過閾值則強制flush, 默認爲1945MB。
  • Analyzer:即分詞器,這個通常是定製化最多的,特別是針對不同的語言,默認的初始化函數使用的是StandardAnalyzer分析器。

Class IndexWriter

在建立IndexWriter時候,需要設定Directory 與IndexWriterConfig 。其中Directory 爲索引保存的文件,而IndexWriterConfig則是indexwriter的配置情況,其中IndexWriter主要提供的核心API如下:

  • addDocument:比較純粹的一個API,就是向Lucene內新增一個文檔。Lucene內部沒有主鍵索引,所有新增文檔都會被認爲一個新的文檔,分配一個獨立的docId。
  • updateDocuments:更新文檔,但是和數據庫的更新不太一樣。數據庫的更新是查詢後更新,Lucene的更新是查詢後刪除再新增。流程是先delete by term,後add document。但是這個流程又和直接先調用delete後調用add效果不一樣,只有update能夠保證在Thread內部刪除和新增保證原子性,詳細流程在下一章節會細說。
  • deleteDocument:刪除文檔,支持兩種類型刪除,by term和by query。在IndexWriter內部這兩種刪除的流程不太一樣,在下一章節再細說。
  • flush:觸發強制flush,將所有DWPT的In-memory buffer flush成segment文件,這個動作可以清理內存,強制對數據做持久化。
  • prepareCommit/commit/rollback:commit後數據纔可被搜索,commit是一個二階段操作,prepareCommit是二階段操作的第一個階段,也可以通過調用commit一步完成,rollback提供了回滾到last commit的操作。
    maybeMerge/forceMerge:maybeMerge觸發一次MergePolicy的判定,而forceMerge則觸發一次強制merge。

Document創建

要索引的數據記錄、文檔在lucene中的表示,是索引、搜索的基本單元。一個Document由多個字段Field構成。IndexWriter按加入的順序爲Document指定一個遞增的id(從0開始),稱爲文檔id。反向索引中存儲的是這個id,文檔存儲中正向索引也是這個id。 業務數據的主鍵id只是文檔的一個字段。

Document主要由一組IndexableFields構成,除了提供添加和刪除的接口外,在Doc內部提供了各種API用於獲取Doc內部的Fields。

Class IndexableField

其爲一個接口,包含了字段名,字段值,字段類型。

public interface IndexableField {
	// field名字
    String name();
    // 字段類型
    IndexableFieldType fieldType();
	// 下面的API都是獲取各種字段值的接口。
    TokenStream tokenStream(Analyzer var1, TokenStream var2);
    /** @deprecated */
    @Deprecated
    float boost();
    BytesRef binaryValue();
    String stringValue();
    Reader readerValue();
    Number numericValue();
}

其中字段類型主要有以下幾個內容:

  • stored:是否存儲
  • tokenized:是否分詞。
  • omitNorms:是否忽略標準化。
  • indexOptions:如何索引。
  • storeTermVectors:是否存儲詞項向量。
  • storeTermVectorOffset: 詞項向量中是否存儲偏移量。
  • storeTermVectorPositions: 詞項向量中是否存儲偏位置。
  • storeTermVectorPaykoads: 詞項向量中是否存儲偏附加信息。

Lucene預定義的字段字類

  • TextField:會自動被索引和分詞的字段。一般被用在文章的正文部分。
  • StringField:會被索引,但是不會被分詞,即會被當作一個完整的token處理,一般用在“國家”或者“ID”。
  • IntPoint/LongPoint/FloatPoint/DoublePoint:indexed for exact/range queries.
  • SortedDocValuesField
  • SortedSetDocValuesField
  • NumericDocValuesField
  • SortedNumericDocValuesField
  • SortedField: 一個默認會被存儲的Field。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章