日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

solr 倒排索引(转载)

發布時間:2024/6/21 综合教程 31 生活家
生活随笔 收集整理的這篇文章主要介紹了 solr 倒排索引(转载) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文地址:http://blog.csdn.net/chichengit/article/details/9235157
http://blog.csdn.net/njpjsoftdev/article/details/54015485

?

介紹:

所謂倒排索引的倒排,其實我感覺定義的不太貼切:正常的文檔索引是,描述一個文檔有哪些關鍵字,也就是文檔—關鍵字列表這種結構,但是倒排索引是關鍵字—文檔列表這種方式。

正排索引從文檔編號找詞:

倒排索引是從詞找文檔編號:

詳細索引內容

設有兩篇文章1和2

文章1的內容為:Tom lives in Guangzhou,I live in Guangzhou too

文章2的內容為:He once lived in Shanghai

獲取關鍵字

全文分析:由于lucene是基于關鍵詞索引和查詢的,首先我們要取得這兩篇文章的關鍵詞,

通常我們需要如下處理措施:

??? a.我們現在有的是文章內容,即一個字符串,我們先要找出字符串中的所有單詞,即分詞。

????? 英文單詞由于用空格分隔,比較好處理。中文單詞間是連在一起的需要特殊的分詞處理。

??? b.文章中的"in", "once" "too"等詞沒有什么實際意義,中文中的"的""是"等字通常也無具體含義,

????? 這些不代表概念的詞可以過濾掉

??? c.用戶通常希望查"He"時能把含"he","HE"的文章也找出來,所以所有單詞需要統一大小寫。

??? d.用戶通常希望查"live"時能把含"lives","lived"的文章也找出來,所以需要把"lives","lived"還原成"live"

??? e.文章中的標點符號通常不表示某種概念,也可以過濾掉

??? 經過上面處理后:

  文章1的所有關鍵詞為:[tom] [live] [guangzhou] [i] [live] [guangzhou]

文章2的所有關鍵詞為:[he] [live] [shanghai]

建立倒排索引

??? 有了關鍵詞后,我們就可以建立倒排索引了。

??? 上面的對應關系是:"文章號"對"文章中所有關鍵詞"。

??? 倒排索引把這個關系倒過來,變成:"關鍵詞"對"擁有該關鍵詞的所有文章號"。

??? 文章1,2經過倒排后變成:

??????? 關鍵詞? ??? ?文章號

  guangzhou ?? ?1

  he????? ??? ? ? ? ? ?? 2

  i??????? ??? ??????????? 1

  live???? ??? ????????? 1,2

  shanghai ??? ?? 2

  tom????? ??? ??????? 1

?? 通常僅知道關鍵詞在哪些文章中出現還不夠,我們還需要知道關鍵詞在文章中出現次數和出現的位置,

?? 通常有兩種位置:

?????? a)字符位置,即記錄該詞是文章中第幾個字符(優點是關鍵詞亮顯時定位快);

?????? b)關鍵詞位置,即記錄該詞是文章中第幾個關鍵詞(優點是節約索引空間、詞組(phase)查詢快),lucene中記錄的就是這種位置。

?加上"出現頻率"和"出現位置"信息后,我們的索引結構變為:

?關鍵詞?? ????? 文章號?? ? [出現頻率]?? ? 出現位置

?guangzhou?? ? 1?? ? ? ? ? ? ? ? [2]?? ???????????? 3,6

?he?? ? ? ? ? ? ? ? ? ? 2?? ? ? ? ? ? ? ? [1] ?? ??????????? 1

?i ?? ??????????????????? 1?? ?????????????? [1]?? ??????????? 4

?live ?? ?????????????? 1?? ?????????????? [2]?? ? ? ? ? ??? 2,5

??? ????????????????????? 2?? ? ? ? ? ? ? ?? [1]?? ? ? ? ? ? ? 2

?shanghai?? ???? 2?? ?????????????? [1] ?? ? ? ? ? ?? 3

?tom ?? ? ? ? ? ? ??? 1?? ?????????????? [1]?? ??????????? 1

?

以live這行為例我們說明一下該結構:

live在文章1中出現了2次,文章2中出現了一次,它的出現位置為"2,5,2"這表示什么呢?

我們需要結合文章號和出現頻率來分析,

文章1中出現了2次,那么"2,5"就表示live在文章1的關鍵詞中出現的兩個位置,

文章2中出現了1次,剩下的"2"就表示live是文章2的關鍵詞中第2個關鍵字。

  
?

以上就是lucene索引結構中最核心的部分。

我們注意到關鍵字是按字符順序排列的(lucene沒有使用B樹結構),

因此lucene可以用二元搜索算法(或叫二分查找/折半查找)快速定位關鍵詞。

簡單實現說明

實現時,lucene將上面三列分別作為:

詞典文件(Term Dictionary)、頻率文件(frequencies)、位置文件 (positions)保存。

其中詞典文件不僅保存有每個關鍵詞,還保留了指向頻率文件和位置文件的指針,

通過指針可以找到該關鍵字的頻率信息和位置信息。   

Lucene中使用了field的概念,用于表達信息所在位置(如標題中,文章中,url中),

在建索引中,該field信息也記錄在詞典文件中,每個關鍵詞都有一個field信息(因為每個關鍵字一定屬于一個或多個field)。

大概實現邏輯

 全文檢索技術由來已久,絕大多數都基于倒排索引來做,曾經也有過一些其他方案如文件指紋。倒排索引,顧名思義,它相反于一篇文章包含了哪些詞,它從詞出發,記載了這個詞在哪些文檔中出現過,由兩部分組成——詞典和倒排表。

  其中詞典結構尤為重要,有很多種詞典結構,各有各的優缺點,最簡單如排序數組,通過二分查找來檢索數據,更快的有哈希表,磁盤查找有B樹、B+樹,但一個能支持TB級數據的倒排索引結構需要在時間和空間上有個平衡,下圖列了一些常見詞典的優缺點:?

  其中可用的有:B+樹、跳躍表、FST?
  B+樹:?
              mysql的InnoDB B+數結構?

理論基礎:平衡多路查找樹

優點:外存索引、可更新

缺點:空間大、速度不夠快

  跳躍表:?

優點:結構簡單、跳躍間隔、級數可控,Lucene3.0之前使用的也是跳躍表結構,后換成了FST,但跳躍表在Lucene其他地方還有應用如倒排表合并和文檔號索引。

缺點:模糊查詢支持不好

  FST?
  Lucene現在使用的索引結構?

理論基礎: 《Direct construction of minimal acyclic subsequential transducers》,通過輸入有序字符串構建最小有向無環圖。

優點:內存占用率低,壓縮率一般在3倍~20倍之間、模糊查詢支持好、查詢快

缺點:結構復雜、輸入要求有序、更新不易

Lucene里有個FST的實現,從對外接口上看,它跟Map結構很相似,有查找,有迭代:

String inputs={"abc","abd","acf","acg"};

//keys

long outputs={1,3,5,7};

//values

FST<Long> fst=new FST<>();

for(int i=0;i<inputs.length;i++)

{ fst.add(inputs[i],outputs[i]) }

//get

Long value=fst.get("abd");

//得到3 //迭代

BytesRefFSTEnum<Long> iterator=new BytesRefFSTEnum<>(fst); while(iterator.next!=null){...}

100萬數據性能測試:

數據結構

HashMap

TreeMap

FST

構建時間(ms)

185

500

1512

查詢所有key(ms)

106

218

890

  可以看出,FST性能基本跟HaspMap差距不大,但FST有個不可比擬的優勢就是占用內存小,只有HashMap10分之一左右,這對大數據規模檢索是至關重要的,畢竟速度再快放不進內存也是沒用的。?
  因此一個合格的詞典結構要求有:?
  1. 查詢速度。?
  2. 內存占用。?
  3. 內存+磁盤結合。?
  后面我們將解析Lucene索引結構,重點從Lucene的FST實現特點來闡述這三點。

1.3 Lucene索引實現

*(本文對Lucene的原理介紹都是基于4.10.3)*

  Lucene經多年演進優化,現在的一個索引文件結構如圖所示,基本可以分為三個部分:詞典、倒排表、正向文件、列式存儲DocValues。


  下面詳細介紹各部分結構:

索引結構

Lucene現在采用的數據結構為FST,它的特點就是:?
  1、詞查找復雜度為O(len(str))?
  2、共享前綴、節省空間?
  3、內存存放前綴索引、磁盤存放后綴詞塊?

我的理解,比如單詞person,perl前綴索引可能是per,后綴塊中可能是son,l等。
  這跟我們前面說到的詞典結構三要素是一致的:1. 查詢速度。2. 內存占用。3. 內存+磁盤結合。我們往索引庫里插入四個單詞abd、abe、acf、acg,看看它的索引文件內容。

 
?

 tip部分,每列一個FST索引,所以會有多個FST,每個FST存放前綴和后綴塊指針,這里前綴就為a、ab、ac。tim里面存放后綴塊和詞的其他信息如倒排表指針、TFDF等,doc文件里就為每個單詞的倒排表。?
  所以它的檢索過程分為三個步驟:?
  1. 內存加載tip文件,通過FST匹配前綴找到后綴詞塊位置。?
  2. 根據詞塊位置,讀取磁盤中tim文件中后綴塊并找到后綴和相應的倒排表位置信息。?
  3. 根據倒排表位置去doc文件中加載倒排表。?
  這里就會有兩個問題,第一就是前綴如何計算,第二就是后綴如何寫磁盤并通過FST定位,下面將描述下Lucene構建FST過程:?
  已知FST要求輸入有序,所以Lucene會將解析出來的文檔單詞預先排序,然后構建FST,我們假設輸入為abd,abd,acf,acg,那么整個構建過程如下:?

1. 插入abd時,沒有輸出。

2. 插入abe時,計算出前綴ab,但此時不知道后續還不會有其他以ab為前綴的詞,所以此時無輸出。

3. 插入acf時,因為是有序的,知道不會再有ab前綴的詞了,這時就可以寫tip和tim了,tim中寫入后綴詞塊d、e和它們的倒排表位置ip_d,ip_e,tip中寫入a,b和以ab為前綴的后綴詞塊位置(真實情況下會寫入更多信息如詞頻等)。

4. 插入acg時,計算出和acf共享前綴ac,這時輸入已經結束,所有數據寫入磁盤。tim中寫入后綴詞塊f、g和相對應的倒排表位置,tip中寫入c和以ac為前綴的后綴詞塊位置。

  以上是一個簡化過程,Lucene的FST實現的主要優化策略有:

1. 最小后綴數。Lucene對寫入tip的前綴有個最小后綴數要求,默認25,這時為了進一步減少內存使用。如果按照25的后綴數,那么就不存在ab、ac前綴,將只有一個跟節點,abd、abe、acf、acg將都作為后綴存在tim文件中。我們的10g的一個索引庫,索引內存消耗只占20M左右。

2. 前綴計算基于byte,而不是char,這樣可以減少后綴數,防止后綴數太多,影響性能。如對宇(e9 b8 a2)、守(e9 b8 a3)、安(e9 b8 a4)這三個漢字,FST構建出來,不是只有根節點,三個漢字為后綴,而是從unicode碼出發,以e9、b8為前綴,a2、a3、a4為后綴,如下圖:

倒排表結構

  倒排表就是文檔號集合,但怎么存,怎么取也有很多講究,Lucene現使用的倒排表結構叫Frame of reference,它主要有兩個特點:?
  1. 數據壓縮,可以看下圖怎么將6個數字從原先的24bytes壓縮到7bytes。?
?
  2. 跳躍表加速合并,因為布爾查詢時,and 和or 操作都需要合并倒排表,這時就需要快速定位相同文檔號,所以利用跳躍表來進行相同文檔號查找。?
  這部分可參考ElasticSearch的一篇博客,里面有一些性能測試:?
  ElasticSearch 倒排表

正向文件

  正向文件指的就是原始文檔,Lucene對原始文檔也提供了存儲功能,它存儲特點就是分塊+壓縮,fdt文件就是存放原始文檔的文件,它占了索引庫90%的磁盤空間,fdx文件為索引文件,通過文檔號(自增數字)快速得到文檔位置,它們的文件結構如下:?
  
  fnm中為元信息存放了各列類型、列名、存儲方式等信息。?
  fdt為文檔值,里面一個chunk就是一個塊,Lucene索引文檔時,先緩存文檔,緩存大于16KB時,就會把文檔壓縮存儲。一個chunk包含了該chunk起始文檔、多少個文檔、壓縮后的文檔內容。?
  fdx為文檔號索引,倒排表存放的時文檔號,通過fdx才能快速定位到文檔位置即chunk位置,它的索引結構比較簡單,就是跳躍表結構,首先它會把1024個chunk歸為一個block,每個block記載了起始文檔值,block就相當于一級跳表。?
  所以查找文檔,就分為三步:?
  第一步二分查找block,定位屬于哪個block。?
  第二步就是根據從block里根據每個chunk的起始文檔號,找到屬于哪個chunk和chunk位置。?
  第三步就是去加載fdt的chunk,找到文檔。這里還有一個細節就是存放chunk起始文檔值和chunk位置不是簡單的數組,而是采用了平均值壓縮法。所以第N個chunk的起始文檔值由 DocBase + AvgChunkDocs * n + DocBaseDeltas[n]恢復而來,而第N個chunk再fdt中的位置由 StartPointerBase + AvgChunkSize * n + StartPointerDeltas[n]恢復而來。?
  從上面分析可以看出,lucene對原始文件的存放是行是存儲,并且為了提高空間利用率,是多文檔一起壓縮,因此取文檔時需要讀入和解壓額外文檔,因此取文檔過程非常依賴隨機IO,以及lucene雖然提供了取特定列,但從存儲結構可以看出,并不會減少取文檔時間。

列式存儲DocValues

  我們知道倒排索引能夠解決從詞到文檔的快速映射,但當我們需要對檢索結果進行分類、排序、數學計算等聚合操作時需要文檔號到值的快速映射,而原先不管是倒排索引還是行式存儲的文檔都無法滿足要求。?
  原先4.0版本之前,Lucene實現這種需求是通過FieldCache,它的原理是通過按列逆轉倒排表將(field value ->doc)映射變成(doc -> field value)映射,但這種實現方法有著兩大顯著問題:?
  1. 構建時間長。?
  2. 內存占用大,易OutOfMemory,且影響垃圾回收。?
  因此4.0版本后Lucene推出了DocValues來解決這一問題,它和FieldCache一樣,都為列式存儲,但它有如下優點:?
  1. 預先構建,寫入文件。?
  2. 基于映射文件來做,脫離JVM堆內存,系統調度缺頁。?
  DocValues這種實現方法只比內存FieldCache慢大概10~25%,但穩定性卻得到了極大提升。?
  Lucene目前有五種類型的DocValues:NUMERIC、BINARY、SORTED、SORTED_SET、SORTED_NUMERIC,針對每種類型Lucene都有特定的壓縮方法。?
  如對NUMERIC類型即數字類型,數字類型壓縮方法很多,如:增量、表壓縮、最大公約數,根據數據特征選取不同壓縮方法。?
  SORTED類型即字符串類型,壓縮方法就是表壓縮:預先對字符串字典排序分配數字ID,存儲時只需存儲字符串映射表,和數字數組即可,而這數字數組又可以采用NUMERIC壓縮方法再壓縮,圖示如下:?
  
  這樣就將原先的字符串數組變成數字數組,一是減少了空間,文件映射更有效率,二是原先變成訪問方式變成固長訪問。?
  對DocValues的應用,ElasticSearch功能實現地更系統、更完整,即ElasticSearch的Aggregations——聚合功能,它的聚合功能分為三類:?
  1. Metric -> 統計?
   典型功能:sum、min、max、avg、cardinality、percent等?
  2. Bucket ->分組?
   典型功能:日期直方圖,分組,地理位置分區?
  3. Pipline -> 基于聚合再聚合?
   典型功能:基于各分組的平均值求最大值。?
基于這些聚合功能,ElasticSearch不再局限與檢索,而能夠回答如下SQL的問題

select gender,count(*),avg(age) from employee where dept='sales' group by gender 銷售部門男女人數、平均年齡是多少

  我們看下ElasticSearch如何基于倒排索引和DocValues實現上述SQL的。?
  
  1. 從倒排索引中找出銷售部門的倒排表。?
  2. 根據倒排表去性別的DocValues里取出每個人對應的性別,并分組到Female和Male里。?
  3. 根據分組情況和年齡DocValues,計算各分組人數和平均年齡?
  4. 因為ElasticSearch是分區的,所以對每個分區的返回結果進行合并就是最終的結果。?
 上面就是ElasticSearch進行聚合的整體流程,也可以看出ElasticSearch做聚合的一個瓶頸就是最后一步的聚合只能單機聚合,也因此一些統計會有誤差,比如count(*) group by producet limit 5,最終總數不是精確的。因為單點內存聚合,所以每個分區不可能返回所有分組統計信息,只能返回部分,匯總時就會導致最終結果不正確,具體如下:?
 原始數據:

Shard 1

Shard 2

Shard 3

Product A (25)

Product A (30)

Product A (45)

Product B (18)

Product B (25)

Product C (44)

Product C (6)

Product F (17)

Product Z (36)

Product D (3)

Product Z (16)

Product G (30)

Product E (2)

Product G (15)

Product E (29)

Product F (2)

Product H (14)

Product H (28)

Product G (2)

Product I (10)

Product Q (2)

Product H (2)

Product Q (6)

Product D (1)

Product I (1)

Product J (8)

?

Product J (1)

Product C (4)

?

 count(*) group by producet limit 5,每個節點返回的數據如下:

Shard 1

Shard 2

Shard 3

Product A (25)

Product A (30)

Product A (45)

Product B (18)

Product B (25)

Product C (44)

Product C (6)

Product F (17)

Product Z (36)

Product D (3)

Product Z (16)

Product G (30)

Product E (2)

Product G (15)

Product E (29)

 合并后:

Merged

Product A (100)

Product Z (52)

Product C (50)

Product G (45)

Product B (43)

 商品A的總數是對的,因為每個節點都返回了,但商品C在節點2因為排不到前5所以沒有返回,因此總數是錯的。

?

?

?

總結

以上是生活随笔為你收集整理的solr 倒排索引(转载)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。