Lucene解析 - 基本概念
生活随笔
收集整理的這篇文章主要介紹了
Lucene解析 - 基本概念
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.
摘要:?前言 Apache Lucene是一個開源的高性能、可擴展的信息檢索引擎,提供了強大的數(shù)據(jù)檢索能力。Lucene已經(jīng)發(fā)展了很多年,其功能越來越強大,架構也越來越精細。它目前不僅僅能支持全文索引,也能夠提供多種其他類型的索引方式,來滿足不同類型的查詢需求。
前言
Apache Lucene是一個開源的高性能、可擴展的信息檢索引擎,提供了強大的數(shù)據(jù)檢索能力。Lucene已經(jīng)發(fā)展了很多年,其功能越來越強大,架構也越來越精細。它目前不僅僅能支持全文索引,也能夠提供多種其他類型的索引方式,來滿足不同類型的查詢需求。基于Lucene的開源項目有很多,最知名的要屬Elasticsearch和Solr,如果說Elasticsearch和Solr是一輛設計精美、性能卓越的跑車,那Lucene就是為其提供強大動力的引擎。為了駕馭這輛跑車讓它跑的更快更穩(wěn)定,我們需要對它的引擎研究透徹。在此之前我們在專欄已經(jīng)發(fā)表了多篇文章來剖析Elasticsearch的數(shù)據(jù)模型、讀寫路徑、分布式架構以及Data/Meta一致性等問題,這篇文章之后我們會陸續(xù)發(fā)表一系列的關于Lucene的原理和源碼解讀,來全面解析Lucene的數(shù)據(jù)模型和數(shù)據(jù)讀寫路徑。Lucene官方對自己的優(yōu)勢總結為幾點:基本概念
在深入解讀Lucene之前,先了解下Lucene的幾個基本概念,以及這幾個概念背后隱藏的一些東西。如圖是一個Index內的基本組成,Segment內數(shù)據(jù)只是一個抽象表示,不代表其內部真實數(shù)據(jù)結構。Index(索引)類似數(shù)據(jù)庫的表的概念,但是與傳統(tǒng)表的概念會有很大的不同。傳統(tǒng)關系型數(shù)據(jù)庫或者NoSQL數(shù)據(jù)庫的表,在創(chuàng)建時至少要定義表的Scheme,定義表的主鍵或列等,會有一些明確定義的約束。而Lucene的Index,則完全沒有約束。Lucene的Index可以理解為一個文檔收納箱,你可以往內部塞入新的文檔,或者從里面拿出文檔,但如果你要修改里面的某個文檔,則必須先拿出來修改后再塞回去。這個收納箱可以塞入各種類型的文檔,文檔里的內容可以任意定義,Lucene都能對其進行索引。Document(文檔)類似數(shù)據(jù)庫內的行或者文檔數(shù)據(jù)庫內的文檔的概念,一個Index內會包含多個Document。寫入Index的Document會被分配一個唯一的ID,即Sequence Number(更多被叫做DocId),關于Sequence Number后面會再細說。Field(字段)一個Document會由一個或多個Field組成,Field是Lucene中數(shù)據(jù)索引的最小定義單位。Lucene提供多種不同類型的Field,例如StringField、TextField、LongFiled或NumericDocValuesField等,Lucene根據(jù)Field的類型(FieldType)來判斷該數(shù)據(jù)要采用哪種類型的索引方式(Invert Index、Store Field、DocValues或N-dimensional等),關于Field和FieldType后面會再細說。Term和Term DictionaryLucene中索引和搜索的最小單位,一個Field會由一個或多個Term組成,Term是由Field經(jīng)過Analyzer(分詞)產(chǎn)生。Term Dictionary即Term詞典,是根據(jù)條件查找Term的基本索引。Segment一個Index會由一個或多個sub-index構成,sub-index被稱為Segment。Lucene的Segment設計思想,與LSM類似但又有些不同,繼承了LSM中數(shù)據(jù)寫入的優(yōu)點,但是在查詢上只能提供近實時而非實時查詢。Lucene中的數(shù)據(jù)寫入會先寫內存的一個Buffer(類似LSM的MemTable,但是不可讀),當Buffer內數(shù)據(jù)到一定量后會被flush成一個Segment,每個Segment有自己獨立的索引,可獨立被查詢,但數(shù)據(jù)永遠不能被更改。這種模式避免了隨機寫,數(shù)據(jù)寫入都是Batch和Append,能達到很高的吞吐量。Segment中寫入的文檔不可被修改,但可被刪除,刪除的方式也不是在文件內部原地更改,而是會由另外一個文件保存需要被刪除的文檔的DocID,保證數(shù)據(jù)文件不可被修改。Index的查詢需要對多個Segment進行查詢并對結果進行合并,還需要處理被刪除的文檔,為了對查詢進行優(yōu)化,Lucene會有策略對多個Segment進行合并,這點與LSM對SSTable的Merge類似。Segment在被flush或commit之前,數(shù)據(jù)保存在內存中,是不可被搜索的,這也就是為什么Lucene被稱為提供近實時而非實時查詢的原因。讀了它的代碼后,發(fā)現(xiàn)它并不是不能實現(xiàn)數(shù)據(jù)寫入即可查,只是實現(xiàn)起來比較復雜。原因是Lucene中數(shù)據(jù)搜索依賴構建的索引(例如倒排依賴Term Dictionary),Lucene中對數(shù)據(jù)索引的構建會在Segment flush時,而非實時構建,目的是為了構建最高效索引。當然它可引入另外一套索引機制,在數(shù)據(jù)實時寫入時即構建,但這套索引實現(xiàn)會與當前Segment內索引不同,需要引入額外的寫入時索引以及另外一套查詢機制,有一定復雜度。Sequence NumberSequence Number(后面統(tǒng)一叫DocId)是Lucene中一個很重要的概念,數(shù)據(jù)庫內通過主鍵來唯一標識一行,而Lucene的Index通過DocId來唯一標識一個Doc。不過有幾點要特別注意:
索引類型
Lucene中支持豐富的字段類型,每種字段類型確定了支持的數(shù)據(jù)類型以及索引方式,目前支持的字段類型包括LongPoint、TextField、StringField、NumericDocValuesField等。如圖是Lucene中對于不同類型Field定義的一個基本關系,所有字段類都會繼承自Field這個類,Field包含3個重要屬性:name(String)、fieldsData(BytesRef)和type(FieldType)。name即字段的名稱,fieldsData即字段值,所有類型的字段的值最終都會轉換為二進制字節(jié)流來表示。type是字段類型,確定了該字段被索引的方式。FieldType是一個很重要的類,包含多個重要屬性,這些屬性的值決定了該字段被索引的方式。Lucene提供的多種不同類型的Field,本質區(qū)別就兩個:一是不同類型值到fieldData定義了不同的轉換方式;二是定義了FieldType內不同屬性不同取值的組合。這種模式下,你也能夠通過自定義數(shù)據(jù)以及組合FieldType內索引參數(shù)來達到定制類型的目的。要理解Lucene能夠提供哪些索引方式,只需要理解FieldType內每個屬性的具體含義,我們來一個一個看:
- stored: 代表是否需要保存該字段,如果為false,則lucene不會保存這個字段的值,而搜索結果中返回的文檔只會包含保存了的字段。
- tokenized: 代表是否做分詞,在lucene中只有TextField這一個字段需要做分詞。
- termVector:?這篇文章很好的解釋了term vector的概念,簡單來說,term vector保存了一個文檔內所有的term的相關信息,包括Term值、出現(xiàn)次數(shù)(frequencies)以及位置(positions)等,是一個per-document inverted index,提供了根據(jù)docid來查找該文檔內所有term信息的能力。對于長度較小的字段不建議開啟term verctor,因為只需要重新做一遍分詞即可拿到term信息,而針對長度較長或者分詞代價較大的字段,則建議開啟term vector。Term vector的用途主要有兩個,一是關鍵詞高亮,二是做文檔間的相似度匹配(more-like-this)。
- omitNorms: Norms是normalization的縮寫,lucene允許每個文檔的每個字段都存儲一個normalization factor,是和搜索時的相關性計算有關的一個系數(shù)。Norms的存儲只占一個字節(jié),但是每個文檔的每個字段都會獨立存儲一份,且Norms數(shù)據(jù)會全部加載到內存。所以若開啟了Norms,會消耗額外的存儲空間和內存。但若關閉了Norms,則無法做index-time boosting(elasticsearch官方建議使用query-time boosting來替代)以及length normalization。
- indexOptions: Lucene提供倒排索引的5種可選參數(shù)(NONE、DOCS、DOCS_AND_FREQS、DOCS_AND_FREQS_AND_POSITIONS、DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS),用于選擇該字段是否需要被索引,以及索引哪些內容。
- docValuesType: DocValue是Lucene 4.0引入的一個正向索引(docid到field的一個列存),大大優(yōu)化了sorting、faceting或aggregation的效率。DocValues是一個強schema的存儲結構,開啟DocValues的字段必須擁有嚴格一致的類型,目前Lucene只提供NUMERIC、BINARY、SORTED、SORTED_NUMERIC和SORTED_SET五種類型。
- dimension:Lucene支持多維數(shù)據(jù)的索引,采取特殊的索引來優(yōu)化對多維數(shù)據(jù)的查詢,這類數(shù)據(jù)最典型的應用場景是地理位置索引,一般經(jīng)緯度數(shù)據(jù)會采取這個索引方式。
Elasticsearch數(shù)據(jù)類型
Elasticsearch內對用戶輸入文檔內Field的索引,也是按照Lucene能提供的幾種模式來提供。除了用戶能自定義的Field,Elasticsearch還有自己預留的系統(tǒng)字段,用作一些特殊的目的。這些字段映射到Lucene本質上也是一個Field,與用戶自定義的Field無任何區(qū)別,只不過Elasticsearch根據(jù)這些系統(tǒng)字段不同的使用目的,定制有不同的索引方式。舉個例子,上圖?是Elasticsearch內兩個系統(tǒng)字段_version和_uid的FieldType定義,我們來解讀下它們的索引方式。Elasticsearch通過_uid字段唯一標識一個文檔,通過_version字段來記錄該文檔當前的版本。從這兩個字段的FieldType定義上可以看到,_uid字段會做倒排索引,不需要分詞,需要被Store。而_version字段則不需要被倒排索引,也不需要被Store,但是需要被正排索引。很好理解,因為_uid需要被搜索,而_version不需要。但_version需要通過docId來查詢,而且Elasticsearch內versionMap內需要通過docId做大量查詢且只需要查詢出_version字段,所以_version最合適的是被正排索引。關于Elasticsearch內系統(tǒng)字段全面的解析,可以看下這篇文章。總結
這篇文章主要介紹了Lucene的一些基本概念以及提供的索引類型。后續(xù)我們會有一系列文章來解析Lucene提供的IndexWriter的寫入流程,其In-Memory Buffer的結構以及持久化后的索引文件結構,來了解Lucene為何能達到如此高效的數(shù)據(jù)索引性能。也會去解析IndexSearcher的查詢流程,以及一些特殊的查詢優(yōu)化的數(shù)據(jù)結構,來了解為何Lucene能提供如此高效的搜索和查詢。原文鏈接
干貨好文,請關注掃描以下二維碼:
總結
以上是生活随笔為你收集整理的Lucene解析 - 基本概念的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 手把手教您解决90%的自然语言处理问题
- 下一篇: 如何在阿里云上构建一个合适的Kubern