全文檢索之sphinx源碼分析--索引創建流程

sphinx是c++語言編寫的一個開源全文檢索項目,索引文件創建速度和檢索速度都是不錯的。由於公司的項目需要我對其源碼進行了解讀,並在其上進行了一定程度的改寫以適應項目檢索提速的需求。這篇文章就是我對sphinx源碼的解讀心得,sphinx基於2.1.9版本。

大體上來講sphinx分成indexer和searchd兩個最主要的進程,indexer負責創建索引文件,searchd負責接收網絡包並進行檢索返回結果,當有新的索引文件生成或者舊的索引文件發生了變更(如merge了新的數據)則indexer會通過輪轉的方式通知searchd。輪轉這個操作就是將索引文件mv成一個.new文件(如有索引文件名爲hour.sp*,會被mv成hour.new.sp*),然後向searchd發送一個SIGHUP信號,由searchd接收信號後將.new文件mv回原格式,然後更新自己的維護列表。這其中indexer是通過創建之初就提供給他的一個.conf配置文件知道searchd的pid存放文件路徑的。

一個索引文件分爲幾個主要的子文件,仍然以hour索引爲例,通常會有如下幾個最主要的文件:

spa 存儲文檔屬性僅在extern存儲模式中使用,用於存儲hit信息

spd 存儲每個wordID可匹配的docID列表這是我們檢索的核心文件,通過該文件可以用關鍵字檢索到對應的docID

spi 存儲詞列表(詞ID和指向.spd文件的指針)可以認爲是在spd上的二級索引,檢索時先遍歷spi得到wordspd上的範圍。Spi分爲dictcheckpoint兩部分,dict爲具體每個wordwordiddoc數、hit數等等信息,而checkpoint則每128word記錄一個wordID和該wordspi上的位置。

sph 存儲索引頭信息除了必要配置信息外還存了checkpointspi上的位置

spe存儲skip-lists,skip-lists可以認爲是在hits記錄(spp)上的二級索引。如果不存儲hitsspp文件大小爲0

spp 存儲每個詞IDhit(或者說記賬,或者詞的出現)列表,如果在conf配置文件裏配置hitless=all則大小爲0。

上面涉及了幾個名詞docID是一行的標記,可以認爲是mysql裏一行數據的id,wordID是關鍵字轉化的一個哈希值,hit不太好理解,可以認爲是一個關鍵字在一行記錄裏的具體位置,例如關鍵字pokeman出現在mysql某一行的記錄中位置爲第3個字段的第5個字符,則hit可能爲0000000300000005這樣的格式,當然這只是個例子,具體格式肯定不是這樣的。這裏的docID、wordID、hit就是sphinx的核心三元組

關於這核心三元組和幾個核心文件的關係可以看這個圖:



sph作爲頭文件只存了一些基本信息如分詞器信息、文件大小、checkpoint位置等等,而spi存了一個有序的關鍵字的序列,每次檢索的時候就是根據這個關鍵字在spi裏看到底這個字是否命中了,有命中才會去spd文件裏找具體命中了哪個document。這張圖還省略了spe和spa文件,會將關鍵字出現的具體位置也返回出來。

索引文件的創建流程如下,創建流程最爲核心的是cidxHit函數,該函數實現了將每一個hit壓縮到對應文件的功能: