solr索引


 solr索引

當我們真正進入到Lucene源代碼之中的時候,我們會發現:

• Lucene的索引過程,就是按照全文檢索的基本過程,將倒排表寫成此文件格式的過程。 

• Lucene的搜索過程,就是按照此文件格式將索引進去的信息讀出來,然后計算每篇文檔打分(score)的過程。

lucene的工作方式 

lucene提供的服務實際包含兩部分:一入一出。所謂入是寫入,即將你提供的源(本質是字符串)寫入索引或者將其從索引中刪除;所謂出是讀出,即向用戶提供全文搜索服務,讓用戶可以通過關鍵詞定位源。

l 寫入流程 

源字符串首先經過analyzer處理,包括:分詞,分成一個個單詞;去除stopword(可選)。 將源中需要的信息加入Document的各個Field中,並把需要索引的Field索引起來,把需要存儲的Field存儲起來。 將索引寫入存儲器,存儲器可以是內存或磁盤。

l 讀出流程 

用戶提供搜索關鍵詞,經過analyzer處理。 對處理后的關鍵詞搜索索引找出對應的Document。 用戶根據需要從找到的Document中提取需要的Field。

索引文件結構

Lucene的索引結構在概念上即為傳統的倒排索引結構。下圖就是Lucene生成的索引的一個實例:

 

Lucene的索引結構是有層次結構的,主要分以下幾個層次: 

• 索引(Index): 

o 在Lucene中一個索引是放在一個文件夾中的。 

o 如上圖,同一文件夾中的所有的文件構成一個Lucene索引。 

• 段(Segment): 

o 一個索引可以包含多個段,段與段之間是獨立的,添加新文檔可以生成新的段,不同的段可以合並。 

o 如上圖,具有相同前綴文件的屬同一個段,圖中共兩個段 "_0" 和 "_1"。 

o segments.gen和segments_5是段的元數據文件,也即它們保存了段的屬性信息。 

• 文檔(Document): 

o 文檔是我們建索引的基本單位,不同的文檔是保存在不同的段中的,一個段可以包含多篇文檔。 

o 新添加的文檔是單獨保存在一個新生成的段中,隨着段的合並,不同的文檔合並到同一個段中。 

• 域(Field): 

o 一篇文檔包含不同類型的信息,可以分開索引,比如標題,時間,正文,作者等,都可以保存在不同的域里。 

o 不同域的索引方式可以不同,在真正解析域的存儲的時候,我們會詳細解讀。 

• 詞(Term): 

       詞是索引的最小單位,是經過詞法分析和語言處理后的字符串。

Field有兩個屬性可選:存儲和索引。通過存儲屬性你可以控制是否對這個Field進行存儲;通過索引屬性你可以控制是否對該Field進行索引。這看起來似乎有些廢話,事實上對這兩個屬性的正確組合很重要,下面舉例說明: 

還是以剛才的文章為例子,我們需要對標題和正文進行全文搜索,所以我們要把索引屬性設置為真,同時我們希望能直接從搜索結果中提取文章標題,所以我們把標題域的存儲屬性設置為真,但是由於正文域太大了,我們為了縮小索引文件大小,將正文域的存儲屬性設置為假,當需要時再直接讀取文件;我們只是希望能從搜索解果中提取最后修改時間,不需要對它進行搜索,所以我們把最后修改時間域的存儲屬性設置為真,索引屬性設置為假。

倒排索引

索引技術主要有以下3種: 倒排索引,后綴數組和簽名文件。其中, 倒排索引技術在當前大多數的信息檢索系統中得到了廣泛的應用, 它對於關鍵詞的搜索非常有效, 在lucene中也是使用的這種技術。后綴數組技術在短語查詢中具有很快的速度, 但是這樣的數據結構在構造和維護時都比較復雜一些。簽名文件技術在20世紀80年代比較流行, 但是后來倒排索引技術逐漸超越了它。

倒排索引是目前搜索引擎公司對搜索引擎最常用的存儲方式, 也是搜索引擎的核心內容, 倒排索引源於實際應用中需要根據屬性的值來查找記錄。這種索引表中的每一項都包括一個屬性值和具有該屬性值的各記錄的地址。由於不是由記錄來確定屬性值, 而是由屬性值來確定記錄的位置, 因而稱為倒排索引。倒排索引是以關鍵字和文檔編號結合, 並以關鍵字作為主鍵的索引結構。下面利用一個例子來說明倒排索引:

 

比如說有兩個文檔, doc1和doc2他們的內容分別如下:

Doc1: we are students。

Doc2: Areyoustudent?

如果按照正常的索引建立如下所示:

文檔名    關鍵字    次數

Doc1       we        1

Doc1       are        1

Doc2      student      1

Doc2       Are        1

……

這里索引的建立是以文檔為標准的, 這樣當文檔很多

的時候數據量將非常的大, 檢索效率會明顯下降的。倒排索引是以單詞為標准來進行索引的建立的。還以上面的doc1和doc2為例:

關鍵字    出現的文檔    次數

student      doc2          1

we         doc1         1

Are        doc1   doc2   1  1

……

索引建立

你可能已經注意到,即使solr.xml已經被POST到服務器兩次,但你搜索"solr"時仍然只得到一個結果。這是因為schema.xml例子文件指定了一個"uniqueKey"字段作為"id"。無論何時你向Solr發送指令添加一個文檔,如果已經存在一個uniqueKey相同的文檔,它會自動地為你替換。

Solr建立索引和對關鍵詞進行查詢都得對字串進行分詞,在向索引庫中添加全文檢索類型的索引的時候,Solr會首先用空格進行分詞,然后把分詞結果依次使用指定的過濾器進行過濾,最后剩下的結果才會加入到索引庫中以備查詢。分詞的順序如下:

l 索引   

1:空格whitespaceTokenize     

2:過濾詞StopFilter    

3:拆字WordDelimiterFilter     

4:小寫過濾LowerCaseFilter     

5:英文相近詞EnglishPorterFilter  

6:去除重復詞RemoveDuplicatesTokenFilter     

l 查詢  

1:查詢相近詞     

2:過濾詞     

3:拆字     

4:小寫過濾     

5:英文相近詞     

6:去除重復詞 

在FieldType定義的時候最重要的就是定義這個類型的數據在建立索引和進行查詢的時候要使用的分析器analyzer,包括分詞和過濾。在例子中FieldType在定義的時候,在index的analyzer中使用 solr.WhitespaceTokenizerFactory這個分詞包,就是空格分詞,然后使用 

solr.StopFilterFactory(去掉如下的通用詞,多為虛詞。 

   "a", "an", "and", "are", "as", "at", "be", "but", "by", 

    "for", "if", "in", "into", "is", "it", 

    "no", "not", "of", "on", "or", "s", "such", 

    "t", "that", "the", "their", "then", "there", "these", 

    "they", "this", "to", "was", "will", "with"),

solr.WordDelimiterFilterFactory,關於分隔符的處理。

solr.RemoveDuplicatesTokenFilterFactory避免重復處理。

solr.StandardFilterFactory 移除首字母簡寫中的點和Token后面的’s。僅僅作用於有類的Token,他們是由StandardTokenizer產生的。 

例:StandardTokenizer+ StandardFilter 

"I.B.M. cat's can't" ==> "IBM", "cat", "can't"

這幾個過濾器。在向索引庫中添加類型的索引的時候,Solr會首先用空格進行分詞,然后把分詞結果依次使用指定的過濾器進行過濾,最后剩下的結果才會加入到索引庫中以備查詢。

 

索引存儲

1. 前綴后綴規則(Prefix+Suffix) 

Lucene在反向索引中,要保存詞典(Term Dictionary)的信息,所有的詞(Term)在詞典中是按照字典順序進行排列的,然而詞典中包含了文檔中的幾乎所有的詞,並且有的詞還是非常的長 的,這樣索引文件會非常的大,所謂前綴后綴規則,即當某個詞和前一個詞有共同的前綴的時候,后面的詞僅僅保存前綴在詞中的偏移(offset),以及除前 綴以外的字符串(稱為后綴)。

 

比如要存儲如下詞:term,termagancy,termagant,terminal,

如果按照正常方式來存儲,需要的空間如下:

[VInt = 4] [t][e][r][m],[VInt = 10][t][e][r][m][a][g][a][n][c][y],[VInt = 9][t][e][r][m][a][g][a][n][t],[VInt = 8][t][e][r][m][i][n][a][l]

共需要35個Byte.

如果應用前綴后綴規則,需要的空間如下:

[VInt = 4] [t][e][r][m],[VInt = 4 (offset)][VInt = 6][a][g][a][n][c][y],[VInt = 8 (offset)][VInt = 1][t],[VInt = 4(offset)][VInt = 4][i][n][a][l]

共需要22個Byte。

大大縮小了存儲空間,尤其是在按字典順序排序的情況下,前綴的重合率大大提高。

2. 差值規則(Delta) 

在Lucene的反向索引中,需要保存很多整型數字的信息,比如文檔ID號,比如詞(Term)在文檔中的位置等等。

由上面介紹,我們知道,整型數字是以VInt的格式存儲的。隨着數值的增大,每個數字占用的Byte的個數也逐漸的增多。所謂差值規則(Delta)就是先后保存兩個整數的時候,后面的整數僅僅保存和前面整數的差即可。

 

比如要存儲如下整數:16386,16387,16388,16389

如果按照正常方式來存儲,需要的空間如下:

[(1) 000, 0010][(1) 000, 0000][(0) 000, 0001],[(1) 000, 0011][(1) 000, 0000][(0) 000, 0001],[(1) 000, 0100][(1) 000, 0000][(0) 000, 0001],[(1) 000, 0101][(1) 000, 0000][(0) 000, 0001]

供需12個Byte。

如果應用差值規則來存儲,需要的空間如下:

[(1) 000, 0010][(1) 000, 0000][(0) 000, 0001],[(0) 000, 0001],[(0) 000, 0001],[(0) 000, 0001]

共需6個Byte。

大大縮小了存儲空間,而且無論是文檔ID,還是詞在文檔中的位置,都是按從小到大的順序,逐漸增大的。

索引壓縮

為了減小索引文件的大小,Lucene對索引還使用了壓縮技術。首先,對詞典文件中的關鍵字進行了壓縮,關鍵字壓縮為<前綴長度,后綴>。例如:當前詞為馬來西亞語,上一個詞為馬來西亞,那么馬來西亞語壓縮為<4,語>。其次大量用到的是對數字的壓縮,數字只保存與上一個值的差值(這樣可以減小數字的長度,進而減少保存該數字需要的字節數)。例如,當前文章號是14569(不壓縮要用3個字節保存),上一文章號是145704,壓縮后保存5(只用一個字節)。

Lucene 的域數據文件(.fdt 文件)在一定情況下采用了 ZLIB壓縮算法,這是一種無損的數據壓縮格式,且獨立於具體的CPU 類型、操作系統、文件格式以及字符集。但是它的弱點是對壓縮數據不支持隨機訪問。在Lucene 中是通過調用 JDK 庫文件中的Deflater類來實現ZLIB 壓縮的(參見index 包下的FieldsWriter類)。

差值壓縮(delta compression)是 Lucene 廣泛采用的另一種壓縮方式,這一方法是基於排序的字符通常具有相同的前綴部分,只把一個塊中與第一個詞不同的部分存儲在索引里顯然能夠節省存儲空間。例如第一個單詞位“automata ”有 個字符,接下來的單詞“automate ”也有 個字符,下一個單詞“automatic ”個字符,再下一個單詞“automation ”10 個字符在存儲的時候按如下的方式存儲:8{automat}a1<>e2<>ic3<>ion。其中的數字表示與前一個單詞不同的個數。

索引優化

利用 Lucene,在創建索引的工程中你可以充分利用機器的硬件資源來提高索引的效率。當你需要索引大量的文件時,你會注意到索引過程的瓶頸是在往磁盤上寫索引文件的過程中。為了解決這個問題, Lucene 在內存中持有一塊緩沖區。但我們如何控制 Lucene 的緩沖區呢?幸運的是,Lucene 的類 IndexWriter 提供了三個參數用來調整緩沖區的大小以及往磁盤上寫索引文件的頻率。

1.合並因子(mergeFactor)

這個參數決定了在 Lucene 的一個索引塊中可以存放多少文檔以及把磁盤上的索引塊合並成一個大的索引塊的頻率。比如,如果合並因子的值是 10,那么當內存中的文檔數達到 10 的時候所有的文檔都必須寫到磁盤上的一個新的索引塊中。並且,如果磁盤上的索引塊的隔數達到 10 的話,這 10 個索引塊會被合並成一個新的索引塊。這個參數的默認值是 10,如果需要索引的文檔數非常多的話這個值將是非常不合適的。對批處理的索引來講,為這個參數賦一個比較大的值會得到比較好的索引效果。

2.最小合並文檔數

這個參數也會影響索引的性能。它決定了內存中的文檔數至少達到多少才能將它們寫回磁盤。這個參數的默認值是10,如果你有足夠的內存,那么將這個值盡量設的比較大一些將會顯著的提高索引性能。

3.最大合並文檔數

這個參數決定了一個索引塊中的最大的文檔數。它的默認值是 Integer.MAX_VALUE,將這個參數設置為比較大的值可以提高索引效率和檢索速度,由於該參數的默認值是整型的最大值,所以我們一般不需要改動這個參數。

通過表 1,你可以清楚地看到三個參數對索引時間的影響。在實踐中,你會經常的改變合並因子和最小合並文檔數的值來提高索引性能。只要你有足夠大的內存,你可以為合並因子和最小合並文檔數這兩個參數賦盡量大的值以提高索引效率,另外我們一般無需更改最大合並文檔數這個參數的值,因為系統已經默認將它設置成了最大。

優化索引就是把磁盤上的多個索引文件合並在一起,以便減少文件的數量,從而也減少搜索索引的時間。需要注意的是索引優化並不能提高索引的速度,而只能提高搜索的速度;並且索引優化只有在索引處理完在一定時間不會發生變化的時候進行。 

Lucene 采用在內存中緩存多個文檔,在寫入磁盤前把這些文檔合並為段。並且可以通過 mergeFcatormaxMergeDocsminMergeDocs控制所要合並的文檔的數量。 mergeFactor是用於控制Lucene 在把索引從內存寫入磁盤上的文件系統時內存中最大的 Document數量、同時它還控制內存中最大的Segment 數量。mergeFactor這個參數設置會嚴重影響到Lucene 建立索引時花費的詞盤I/O 時間和內存使用量,如果 mergeFactor值設置的太小,則磁盤的 I/O 操作太頻繁,經常進行Segment 的合並。而如果 mergeFactor值設置的太大,則內存中持有的 Document數量可能會很多,因此占用大量的內存。maxMergeDocs 用來限制一個Segment 中最大的文檔數量。例如:當 mergeFactor設置為10 maxMergeDocs設置為2000 是,第一次合並,Segment 中的文檔數量可能為 100 ,再合並時,一個 Segmnet中的文檔數量就會為1000 ,此時由於受到 maxMergeDocs的限制,一個 Segment 中的文檔數量最多不超過2000,因此下一次合並將發生在 2000 文檔時。 minMergeDocs用於控制內存中持有的文檔數量,也就是說,內存中文檔被到磁盤前的數量。 

Lucene 可以通過 FSDirectory 把索引存儲在磁盤目錄中,也可以通過RAMDirectory使用內存緩沖的形式操作索引文件,以便進一步控制索引的合並。RAMDirectory的操作與FSDirectory 的操作類似,但是速度更快,並且RAMDirectory不把具體的索引寫入磁盤目錄,一旦程序退出,索引文件就不存在。為此Lucene提供了addIndexes 函數把內存索引文件寫入磁盤目錄文件。

 

 

commit就是提交數據,就是更新。  

curl 'http://localhost:8983/solr/update?optimize=true&maxSegments=10&waitFlush=false'

索引提交

這個設置用於控制什么時候將更新的數據寫入索引,該設置由以下兩個子參數

maxDocs當索引的文檔達到這個數量時,就自動更新到索引中

maxTime單位為毫秒,當離上一次 commit 超過這個時間后,將自動最近更新的文檔寫入索引中

只要達到上面任一條件,solr 都將自動最近更新的文檔寫入索引中,如果沒有指明 autoCommit 這個參數,只能通過顯示調用 commit 才能將最近更新的文檔寫入索引中。設置這個參數時,就需要最性能和准確度之間做個權衡,頻繁提交,可以提高數據准確性,但是對性能有一定的損耗。

<autoCommit>

    <maxDocs>10000</maxDocs>

    <maxTime>1000</maxTime>

</autoCommit>

 

 

建立索引:堆排序,hashjoin 

最簡單的能完成索引的代碼片斷

IndexWriter writer = new IndexWriter(“/data/index/”, new StandardAnalyzer(), true); 

Document doc = new Document(); 

doc.add(new Field("title", "lucene introduction", Field.Store.YES, Field.Index.TOKENIZED)); 

doc.add(new Field("content", "lucene works well", Field.Store.YES, Field.Index.TOKENIZED)); 

writer.addDocument(doc); 

writer.optimize(); 

writer.close();

下面我們分析一下這段代碼。 

首先我們創建了一個writer,並指定存放索引的目錄為“/data/index”,使用的分析器為StandardAnalyzer,第三個參數說明如果已經有索引文件在索引目錄下,我們將覆蓋它們。 

然后我們新建一個document。 

我們向document添加一個field,名字是“title”,內容是“lucene introduction”,對它進行存儲並索引。 

再添加一個名字是“content”的field,內容是“lucene works well”,也是存儲並索引。 

然后我們將這個文檔添加到索引中,如果有多個文檔,可以重復上面的操作,創建document並添加。 

添加完所有document,我們對索引進行優化,優化主要是將多個segment合並到一個,有利於提高索引速度。 

隨后將writer關閉,這點很重要。

索引查看

http://localhost:8983/solr/admin/luke?wt=xslt&tr=luke.xsl

類似ik jar包的配制方法,在solrconfig.xml中加入<requestHandler name="/admin/luke" class="org.apache.solr.handler.admin.LukeRequestHandler" />

會展示索引字段field有哪些 以及它的fieldtype和數量、是否存儲等信息。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM