sphinx 源碼閱讀之分詞,壓縮索引,倒排——單詞對應的文檔ID列表本質和lucene無異 也是外部排序再壓縮 解壓的時候須要所有掃描doc_ids列表偏移量相加得到最終的文檔ID

轉自:http://github.tiankonguse.com/blog/2014/12/03/sphinx-token-inverted-sort.html

如今咱們的背景是有16個已經排序的數據存在磁盤上。
因爲數據量很大,咱們不能一次性所有讀進來。html

咱們的目標是依次挑出最小的hit,而後交給索引引擎處理。git

sphinx 使用了 CSphHitQueue 這個數據結構。github

CSphHitQueue 你猜是什麼? 隊列? 恭喜你,猜錯了。
CSphHitQueue 是一個最小堆。
且堆的最大個數是 iRawBlocks。數組

因爲 iRawBlocks 個 hits 數組已經排序,因此咱們只須要獲得 已排序的hits數組的第一個元素,就能夠用堆獲得最小的那個元素了。
而後咱們把最小的這個元素建索引壓縮儲存,刪除最小元素,並取出最小元素所在 hits數組中下一個元素,扔到堆中。
這樣就能夠從小到大取出全部的元素,並逐個創建索引壓縮儲存了。數據結構

這段話看不懂的話,能夠看下面的圖。函數

2983121808

其中建立索引壓縮儲存主要依靠這個函數spa

  1. cidxHit ( tQueue.m_pData );

其中 tQueue.m_pData 的數據結構以下code

  1. /// fat hit, which is actually stored in VLN index
  2. struct CSphFatHit{
  3. DWORD m_iDocID; ///< document ID
  4. DWORD m_iGroupID; ///< documents group ID
  5. DWORD m_iTimestamp; ///< document timestamp
  6. DWORD m_iWordID; ///< word ID in current dictionary
  7. DWORD m_iWordPos; ///< word position in current document
  8. };

hit 是先按 m_iWordID 排序, 相等了再按 m_iDocID 排序, 最後才按 m_iWordPos 排序的。htm

如今咱們先不考慮上面的堆,咱們假設全部的 hit 已經在一個數組中了,且按上面的規則排序了。
如今咱們想作的是對這個 hit 數組建立索引,並壓縮儲存。blog

主要作了這個幾件事。

第一,根據 m_iWordID 將分詞分爲 2014 塊。
並使用 cidxPagesDir 記錄塊的偏移量(還記得索引文件第二個寫入的數據嗎)。

第二,對於每一塊,咱們按分詞分組,並在索引文件 spi 中儲存每一個詞組的信息。
具體儲存的信息以下

  • 和上一個分詞(wordID)的誤差
  • 這個分詞組在 spd 文件內的長度
  • 這個分詞記錄的變化次數
  • 這個分詞的 hit 數量

第三,對於每一個hit,咱們存兩部分信息。

  • 位置(pos)偏移量信息
  • 文檔(docId)偏移量的信息

上面的三部分信息都儲存後,咱們就能夠快速的解析出來。

假設咱們又上面的壓縮的信息了。
咱們要搜索一個詞時,會如何工做呢?
假設咱們已經獲得這個詞的 wordId 了,只須要二分一下,就能夠再 O(log(1024)) 的時間內獲得 wordId 在那個塊內。

找到一個塊內,出現一個問題,咱們不能再次二分查找來找到對應的分詞列表。 由於這個 index 儲存的是和上一個分詞的相對偏移量,那隻好所有讀入內存,掃描一遍對偏移量求和,而後才能找到對應的詞。

這個過程當中咱們進行了兩次 IO 操做。
第一次讀取塊列表信息 cidxPagesDir。
第二次讀取選中的那一塊的全部數據。

雖然儲存偏移量節省了一些磁盤儲存,可是倒是用掃描整塊數據爲代價的。咱們原本能夠直接二分整塊數據的。

無論怎樣,咱們在索引中找到了須要查找的那個分詞的位置。
而後咱們能夠在數據文件內讀取對應的信息,而後獲得對應記錄的id了。

固然,上面這個只是個人推理,下面咱們來看看 sphinx 是怎麼搜索的吧。

看 sphinx 的搜索方法,只須要看 CSphIndex_VLN 的 QueryEx 函數便可。
首先對查詢的語句進行分詞,而後讀取索引頭 m_tHeader, 讀取分塊信息 cidxPagesDir。
而後就對分詞進行搜索了。
爲了防止相同的分詞重複查找,這裏採用二層循環,先來判斷這個分詞以前是否搜索過,搜索過就記下搜索過的那個詞的位置。
沒搜索過,就搜索。

xxx代碼略!

 

看了這個代碼,和我想的有點出入,可是整體思路仍是同樣的。
它是把全部的 cidxPagesDir 全儲存起來了,這樣直接定位到指定的位置了。少了一個二分搜索。
定位到某個塊以後, 果真採用暴力循環來一個一個的增長偏移,而後查找對應的分詞。
找到了記錄對應的位置的四大元信息。

再而後因爲數據量已經很小了,就把匹配的數據取出來便可。固然,取數據的時候會進行布爾操做,並且會加上權值計算,這樣就搜索知足條件的前若干條了。

相關文章
相關標籤/搜索