热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Lucene剖析基本概念

媒介ApacheLucene是一個開源的高機能、可擴大的信息檢索引擎,供應了壯大的數據檢索才能。Lucene已生長了許多年,其功用愈來愈壯大,架構也愈來愈邃密。它現在不僅僅能支撐全

媒介

Apache Lucene是一個開源的高機能、可擴大的信息檢索引擎,供應了壯大的數據檢索才能。Lucene已生長了許多年,其功用愈來愈壯大,架構也愈來愈邃密。它現在不僅僅能支撐全文索引,也能夠供應多種其他範例的索引體式格局,來滿足差別範例的查詢需求。

基於Lucene的開源項目有許多,最着名的要屬Elasticsearch和Solr,假如說Elasticsearch和Solr是一輛設想優美、機能卓着的跑車,那Lucene就是為其供應壯大動力的引擎。為了駕御這輛跑車讓它跑的更快更穩固,我們須要對它的引擎研討透闢。

在此之前我們在專欄已宣布了多篇文章來理會Elasticsearch的數據模型、讀寫途徑、分佈式架構以及Data/Meta一致性等題目,這篇文章以後我們會一連宣布一系列的關於Lucene的道理和源碼解讀,來周全剖析Lucene的數據模型和數據讀寫途徑。

Lucene官方對本身的上風總結為幾點:
Scalable, High-Performance Indexing
Powerful, Accurate and Efficient Search Algorithms
願望經由過程我們的系列文章,能夠讓讀者明白Lucene是怎樣到達這些目標的。

悉數分析會基於Lucene 7.2.1版本,在讀這篇文章之前,須要有肯定的學問基礎,比方相識基礎的搜刮和索引道理,曉得什麼是倒排、分詞、相干性等基礎觀點,相識Lucene的基礎運用,比方Directory、IndexWriter、IndexSearcher等。

基礎觀點

在深切解讀Lucene之前,先相識下Lucene的幾個基礎觀點,以及這幾個觀點背地隱蔽的一些東西。

《Lucene剖析 - 基本概念》

如圖是一個Index內的基礎組成,Segment內數據只是一個籠統示意,不代表其內部實在數據構造。

Index(索引)
類似數據庫的表的觀點,然則與傳統表的觀點會有很大的差別。傳統關聯型數據庫或許NoSQL數據庫的表,在建立時最少要定義表的Scheme,定義表的主鍵或列等,會有一些明白定義的束縛。而Lucene的Index,則完整沒有束縛。Lucene的Index能夠明白為一個文檔收納箱,你能夠往內部塞入新的文檔,或許從內里拿出文檔,但假如你要修正內里的某個文檔,則必需先拿出來修正後再塞歸去。這個收納箱能夠塞入各種範例的文檔,文檔里的內容能夠恣意定義,Lucene都能對其舉行索引。

Document(文檔)
類似數據庫內的行或許文檔數據庫內的文檔的觀點,一個Index內會包含多個Document。寫入Index的Document會被分派一個唯一的ID,即Sequence Number(更多被叫做DocId),關於Sequence Number背面會再細說。

Field(字段)
一個Document會由一個或多個Field組成,Field是Lucene中數據索引的最小定義單元。Lucene供應多種差別範例的Field,比方StringField、TextField、LongFiled或NumericDocValuesField等,Lucene依據Field的範例(FieldType)來推斷該數據要採納哪一種範例的索引體式格局(Invert Index、Store Field、DocValues或N-dimensional等),關於Field和FieldType背面會再細說。

Term和Term Dictionary
Lucene中索引和搜刮的最小單元,一個Field會由一個或多個Term組成,Term是由Field經由Analyzer(分詞)發生。Term Dictionary即Term辭書,是依據前提查找Term的基礎索引。

Segment
一個Index會由一個或多個sub-index組成,sub-index被稱為Segment。Lucene的Segment設想頭腦,與LSM類似但又有些差別,繼續了LSM中數據寫入的長處,然則在查詢上只能供應近及時而非及時查詢。

Lucene中的數據寫入會先寫內存的一個Buffer(類似LSM的MemTable,然則不可讀),當Buffer內數據到肯定量後會被flush成一個Segment,每一個Segment有本身自力的索引,可自力被查詢,但數據永久不能被變動。這類形式避免了隨機寫,數據寫入都是Batch和Append,能到達很高的吞吐量。Segment中寫入的文檔不可被修正,但可被刪除,刪除的體式格局也不是在文件內部原地變動,而是會由別的一個文件保留須要被刪除的文檔的DocID,保證數據文件不可被修正。Index的查詢須要對多個Segment舉行查詢並對效果舉行兼并,還須要處置懲罰被刪除的文檔,為了對查詢舉行優化,Lucene會有戰略對多個Segment舉行兼并,這點與LSM對SSTable的Merge類似。

Segment在被flush或commit之前,數據保留在內存中,是不可被搜刮的,這也就是為什麼Lucene被稱為供應近及時而非及時查詢的緣由。讀了它的代碼后,發明它並非不能完成數據寫入即可查,只是完成起來比較複雜。緣由是Lucene中數據搜刮依靠構建的索引(比方倒排依靠Term Dictionary),Lucene中對數據索引的構建會在Segment flush時,而非及時構建,目標是為了構建最高效索引。固然它可引入別的一套索引機制,在數據及時寫入時即構建,但這套索引完成會與當前Segment內索引差別,須要引入分外的寫入時索引以及別的一套查詢機制,有肯定複雜度。

Sequence Number
Sequence Number(背面統一叫DocId)是Lucene中一個很重要的觀點,數據庫內經由過程主鍵來唯一標識一行,而Lucene的Index經由過程DocId來唯一標識一個Doc。不過有幾點要特別注意:
DocId實際上並不在Index內唯一,而是Segment內唯一,Lucene這麼做重如果為了做寫入和緊縮優化。那既然在Segment內才唯一,又是怎樣做到在Index級別來唯一標識一個Doc呢?計劃很簡樸,Segment之間是有遞次的,舉個簡樸的例子,一個Index內有兩個Segment,每一個Segment內分別有100個Doc,在Segment內DocId都是0-100,轉換到Index級的DocId,須要將第二個Segment的DocId局限轉換為100-200。
DocId在Segment內唯一,取值從0最先遞增。但不代表DocId取值肯定是一連的,假如有Doc被刪除,那可能會存在樸陋。
一個文檔對應的DocId可能會發生變化,重如果發生在Segment兼并時。

Lucene內最中心的倒排索引,本質上就是Term到一切包含該Term的文檔的DocId列表的映照。所以Lucene內部在搜刮的時刻會是一個兩階段的查詢,第一階段是經由過程給定的Term的前提找到一切Doc的DocId列表,第二階段是依據DocId查找Doc。Lucene供應基於Term的搜刮功用,也供應基於DocId的查詢功用。

DocId採納一個從0最先底層的Int32值,是一個比較大的優化,同時體現在數據緊縮和查詢效力上。比方數據緊縮上的Delta戰略、ZigZag編碼,以及倒排列表上採納的SkipList等,這些優化後續會詳述。

索引範例

Lucene中支撐雄厚的字段範例,每種字段範例肯定了支撐的數據範例以及索引體式格局,現在支撐的字段範例包含LongPoint、TextField、StringField、NumericDocValuesField等。

《Lucene剖析 - 基本概念》

如圖是Lucene中關於差別範例Field定義的一個基礎關聯,一切字段類都邑繼續自Field這個類,Field包含3個重要屬性:name(String)、fieldsData(BytesRef)和type(FieldType)。name即字段的稱號,fieldsData即字段值,一切範例的字段的值終究都邑轉換為二進制字節流來示意。type是字段範例,肯定了該字段被索引的體式格局。
FieldType是一個很重要的類,包含多個重要屬性,這些屬性的值決議了該字段被索引的體式格局。
Lucene供應的多種差別範例的Field,本質區分就兩個:一是差別範例值到fieldData定義了差別的轉換體式格局;二是定義了FieldType內差別屬性差別取值的組合。這類形式下,你也能夠經由過程自定義數據以及組合FieldType內索引參數來到達定製範例的目標。
要明白Lucene能夠供應哪些索引體式格局,只須要明白FieldType內每一個屬性的詳細寄義,我們來一個一個看:
stored: 代表是不是須要保留該字段,假如為false,則lucene不會保留這個字段的值,而搜刮效果中返回的文檔只會包含保留了的字段。
tokenized: 代表是不是做分詞,在lucene中只要TextField這一個字段須要做分詞。
termVector: 這篇文章很好的詮釋了term vector的觀點,簡樸來講,term vector保留了一個文檔內一切的term的相干信息,包含Term值、湧現次數(frequencies)以及位置(positions)等,是一個per-document inverted index,供應了依據docid來查找該文檔內一切term信息的才能。關於長度較小的字段不發起開啟term verctor,由於只須要重新做一遍分詞即可拿到term信息,而針對長度較長或許分詞價值較大的字段,則發起開啟term vector。Term vector的用處重要有兩個,一是關鍵詞高亮,二是做文檔間的類似度婚配(more-like-this)。
omitNorms: Norms是normalization的縮寫,lucene許可每一個文檔的每一個字段都存儲一個normalization factor,是和搜刮時的相干性盤算有關的一個係數。Norms的存儲只佔一個字節,然則每一個文檔的每一個字段都邑自力存儲一份,且Norms數據會悉數加載到內存。所以若開啟了Norms,會斲喪分外的存儲空間和內存。但若封閉了Norms,則沒法做index-time boosting(elasticsearch官方發起運用query-time boosting來替換)以及length normalization。
indexOptions: Lucene供應倒排索引的5種可選參數(NONE、DOCS、DOCS_AND_FREQS、DOCS_AND_FREQS_AND_POSITIONS、DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS),用於挑選該字段是不是須要被索引,以及索引哪些內容。
docValuesType: DocValue是Lucene 4.0引入的一個正向索引(docid到field的一個列存),大大優化了sorting、faceting或aggregation的效力。DocValues是一個強schema的存儲構造,開啟DocValues的字段必需具有嚴厲一致的範例,現在Lucene只供應NUMERIC、BINARY、SORTED、SORTED_NUMERIC和SORTED_SET五種範例。
dimension:Lucene支撐多維數據的索引,採用特別的索引來優化對多維數據的查詢,這類數據最典範的運用場景是地理位置索引,平常經緯度數據會採用這個索引體式格局。

來看下Lucene中對StringField的一個定義:

《Lucene剖析 - 基本概念》

StringFiled有兩種範例索引定義,TYPE_NOT_STORED和TYPE_STORED,唯一的區分是這個Field是不是須要Store。從其他的幾個屬性也能夠解讀出,StringFiled挑選omitNorms,須要舉行倒排索引而且不須要被分詞。

Elasticsearch數據範例

Elasticsearch內對用戶輸入文檔內Field的索引,也是根據Lucene能供應的幾種形式來供應。除了用戶能自定義的Field,Elasticsearch另有本身預留的體系字段,用作一些特別的目標。這些字段映照到Lucene本質上也是一個Field,與用戶自定義的Field無任何區分,只不過Elasticsearch依據這些體系字段差別的運用目標,定製有差別的索引體式格局。

《Lucene剖析 - 基本概念》

舉個例子,上圖​是Elasticsearch內兩個體系字段_version和_uid的FieldType定義,我們來解讀下它們的索引體式格局。Elasticsearch經由過程_uid字段唯一標識一個文檔,經由過程_version字段來紀錄該文檔當前的版本。從這兩個字段的FieldType定義上能夠看到,_uid字段會做倒排索引,不須要分詞,須要被Store。而_version字段則不須要被倒排索引,也不須要被Store,然則須要被正排索引。很好明白,由於_uid須要被搜刮,而_version不須要。但_version須要經由過程docId來查詢,而且Elasticsearch內versionMap內須要經由過程docId做大批查詢且只須要查詢出_version字段,所以_version最合適的是被正排索引。

關於Elasticsearch內體系字段周全的剖析,能夠看下這篇文章。

總結

這篇文章重要引見了Lucene的一些基礎觀點以及供應的索引範例。後續我們會有一系列文章來剖析Lucene供應的IndexWriter的寫入流程,其In-Memory Buffer的構造以及耐久化后的索引文件構造,來相識Lucene為什麼能到達云云高效的數據索引機能。也會去剖析IndexSearcher的查詢流程,以及一些特別的查詢優化的數據構造,來相識為什麼Lucene能供應云云高效的搜刮和查詢。


推荐阅读
author-avatar
央央说去_531
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有