Lucene與HBase的組合使用及HBasene的分析報告

Lucene簡介數據庫

  Lucene中,以document的形式做爲搜索的主體。document由fieldName和fieldValue所組成,每一個fieldValue又能夠由一個或多個term元素來組成。基於不一樣的分詞及索引規則,可用於搜索fieldValue的term少於組成fieldValue的term。Lucene的搜索基於反向索引,包含着可用於搜索document的field信息。經過Lucene,能夠正向查找document,以便了解其包含哪些field信息;也能夠經過反向索引,經過搜索字段的term,來查詢包含該term的document。後端

 

[ 圖1 ]  Lucene整體架構緩存

 

  由圖1所示,IndexSearcher實現了搜索的邏輯,IndexWriter實現了文檔的插入與反向索引的創建,IndexReader由IndexSearcher調用以便讀取索引的內容。IndexReader和IndexWriter都依賴於抽象類Directory,Directory提供操做索引數據及的API。架構

  標準的Lucene是基於文件系統和基於內存的。負載均衡

  標準基於文件系統的後端的缺點在於,隨着索引增長性能會降低,人們使用了各類不一樣的技術來解決這個問題,包括負載均衡和索引分片(index sharding,在多個Lucene實例之間切分索引)。儘管分片功能很強大,但它讓整體的實現架構變得更復雜,而且須要大量對指望文檔的預測知識,才能對Lucene索引進行合適地分片。另外一方面,在大數據量的狀況下,segment的合併花銷巨大;頻繁的update數據將使得Lucene對Disk io產生巨大的影響。一個新的數據的update,可能致使一部分根本沒有變化的索引被重寫不少次,而且可能致使不少的小的index segment,形成了search的性能降低。分佈式

  Lucene的優點在於索引查找的迅速,而非document的存儲。爲解決上述問題,基於NoSQL數據庫存儲索引的後端結構應運而生。函數

 

如下,將基於HBase的實現來進行分析。源碼分析

實現方法性能

  在Lucene中,其會操做兩個單獨的數據集:大數據

  • 文檔數據集中存儲了全部文檔,包括存儲的字段等。

  • 索引數據集中存儲了全部字段/詞彙/詞頻/位置等信息,以及包含當前字段的document

 

  若是要實現將Lucene的後端移植到HBase上,直接構建一個Directory的實現並不會是最簡單的。在已有的開源項目中,Lucandra和HBasene均採用了直接重寫IndexReader和IndexWriter的方式,直接繞開了Directory的API。此實現並不會重寫Lucene的索引查詢機制。如若重載IndexSearcher,則能夠在使用現有的Lucene索引查詢機制上,根據後端的功能加強性能。

 

[ 圖2 ] Lucene的後端從新設計

 

  圖2的設計,能夠將Lucene後端與HBase整合起來,將索引數據存儲到HBase中,從而利用HBase的大數據存儲以及分佈式性能。

 

架構設計

  在架構設計上,將HBase用做索引的持久化後端,同時能夠如網上所說,基於內存實現一套緩存機制,用來提升數據讀取速度。實現一套高效的緩存同步機制,也將有利於數據讀寫速率的提升。

 

 

[ 圖3 ] 帶有內存緩存以及同步緩存的HBase後端實現

 

  對於HBase的訪問,每一次交互都須要經過以太網,以太網的運行狀態將大大影響系統的使用狀況,而索引的創建又但願能達到實時且高響應。爲了平衡這兩種相互衝突的需求,在內存中,緩存可以最小化HBase用於搜索和文件返回的數據讀取量,從而極大提高性能;按照須要運行爲多個Lucene示例以支持日益增加的搜索客戶端的能力。後者須要最小化緩存的生命週期,從而和HBase實例(上面提到實例的副本)中的內容同步。經過爲活動參數實現可配置的緩存時間,限制每一個Lucene實例中展示的緩存,咱們能夠達成一種折中方案。

  根據上述所描述的結構,對於讀操做,首先會檢查所需數據是否在內存中且沒有過時,若是有效將直接使用,否者將從HBase中獲取數據並更新到內存中。而對於寫操做,能夠簡化到直接將數據寫入到HBase中,進而不須要考慮是否須要創建或更新緩存這種複雜的問題,這也將提升系統的實時響應性。

 

HBase Table的實現

當前瞭解到的兩種可參考的實現方式:HBasene類型、Lucandra類型。

 

1-- HBasene

其索引表由如下幾個column family組成:

  • fm.sequence:記錄sequenceId,表示當前添加的第幾個document。在執行createLuceneIndexTable時建立該行,且rowKey爲segmentId,Column.qulifier爲qual.sequence,Column.value=-1。每add一個document,當前segmentId的Column.value將自增1。

  • fm.doc2int:每一個document的存儲都將被分配一個惟一的id,若是document的Field.Store=YES,則可以經過該id獲取到對應的document的所有信息。

  • fm.fields:記錄了Field中value的內容,rowKey爲documentId,Column.qulifier爲FieldName,Column.value爲FieldValue的內容。

  • fm.termVector:向量偏移數據,用於模糊查找,記錄了偏移量等信息,rowKey爲FileldName/Term的組合,Column.qulifier爲documentId,Column.value爲指向的document中的全部位置偏移量。Column.value的結構爲:[A][size][position]……[position]

  • fm.termFrequencies:關鍵詞在每一個document中出現的頻率,rowKey結構爲zfm/FileName/Term,Column.qulifier爲documentId,Column.value爲出現的次數。

 

2-- Lucandra

在Lucendra中,只有兩個ColummFamily來存儲數據,分別是TermInfo和Documents

<Keyspace Name="Lucandra"> 
    <ColumnFamily   Name="TermInfo" CompareWith="BytesType" ColumnType="Super"  CompareSubcolumnsWith="BytesType"  KeysCached="10%" /> 
    <ColumnFamily Name="Documents" CompareWith="BytesType" KeysCached="10%" /> 
</Keyspace>

 

  • TermInfo

        TermInfo存儲了Lucandra逆向索引的信息,用來存儲index的Field信息,其結構以下:

        RowKey:field/term

        SuperColumn.name:documentId

        [ SubColumn.name:"frequencies"  Column.value:count ]

        [ SubColumn.name:"position"  Column.value:position vector ]

        [ SubColumn.name:"offsets"  Column.value:offsets vector ]

        [ SubColumn.name:"norms"  Column.value:norms vector ]

 

因爲HBase中不存在SuperColumn和SubColumn的概念,咱們能夠將其簡化爲:

        RowKey:field/term

        Column.qulifier:documentId

        Column.value:fieldInfo [ 此fieldInfo能夠經過AVRO類定義 ]

 

  • Documents

        Documents存儲了Document數據,其結構以下:

        RowKey:documentId

        Column.name:fieldName

        Column.value:fieldValue

 

對比:

        由HBasene的表結構能夠知道,對於每個document的更新以及每一次term的查詢,都須要操縱多行數據才能實現,邏輯上相對複雜;而Lucandra只須要處理兩行(documents及TermInfo各一行)。可是HBasene的優點在於單行的存儲結構小,每次與HBase交互只須要傳輸少許的數據及可,而且不像Lucandra結構同樣須要而外的AVRO組件來序列化與反序列化數據。

 

HBasene的缺陷:

        一、HBasene在三年前已經中止了其項目的更新,開源的支持斷開了。

        二、在對HBasene的最新源碼分析過程當中,首先是發現其設計上保留着segment的概念,可是其設計阻礙着document的查詢。其documentId由segmentId/sequenceId組成,每次segment的commit,sequenceId恢復爲-1。而search時返回的TopDocs中只包含了sequenceId[int]。

        三、每次添加document時,只有fm.Fields以及fm.doc2int的數據被實時flush到了HBase中,而其餘數據須要segment大小到了必定程度[默認1000條document]才commit,容易形成數據缺失。在segment沒被commit的狀況下,是沒法查詢到新添加的document的。

        四、HBase Table的設計中,沒有考慮到當document中fieldName相同的狀況。當fieldName相同的狀況下,後面添加的會覆蓋前面添加數據的,最後只保留最後添加的一份。

        五、fm.termVector中,Column.value保存的並非positionVector信息,而是segment中全部document裏出現的次數。

        六、fm.termFrequencies只是單純地存儲了,並無被應用上。

        七、IndexWriter的重寫並無實現全部函數,只實現了最基本的addDocument

        八、對IndexSearch的重寫和擴展也不夠。

相關文章
相關標籤/搜索