FST是lucene中用來存儲字典,並進行檢索的核心數據結構,我記得在lucene3.0版本前都用的是跳躍鏈表(不清楚的同窗之後我會專門寫一篇磁盤索引技術來講清楚,網上也有相關資料),實際上採用該數據結構的最大的好處是比跳躍鏈表更加節省存儲空間,它的速度與跳躍鏈表相好比何具體沒測過(但從理論上分析,fst本質上是個圖結構,當查詢一個詞典時須要對圖中節點進行解析,其中有大量的位運算並按路徑進行搜索比較耗費cpu,而跳躍鏈表沒那麼多位運算可是須要遍歷小區間的每一個詞典進行匹配,因此各有優點),但能夠確定的是它比hashmap慢。其實跳躍鏈表索引技術在當前最新的lucene版本7.6中的DocValues特性中仍然使用具體請參考談談lucene的DocValues特性之SortedDocValuesField,不知是否能夠認爲跳躍鏈表會比FST速度更快?node
FST本質上是一個比HashMap有更強大功能keyvalue存儲結構,它的概念視圖能夠訪問「http://examples.mikemccandless.com/fst.py?terms=pop%2Fabe%0D%0Astar%2Fabc%0D%0Asto%2Facs%0D%0Astop%2Fabd%0D%0Asuop%2Fcvb%0D%0A%0D%0A%0D%0A&cmd=Build+it%21」該網站看到。注意:在生成FST時,詞典必須按key進行排序(緣由在於排序後能生成一個最小化的DFA),它是共享前綴和後綴的,實際上前綴樹就是它的一個簡化版。數組
如下對照FST截圖說明節點與邊的關鍵屬性:數據結構
說明:紅線表明該節點的最後一個路徑,加粗的線表明該路徑指向結束節點,value會經過提取公共前綴而後存儲在線上(有種特殊狀況會將值存儲在節點上如sto/acs與stop/abd,可是lucene在序列化的時候會將值存儲在線上的nextFinalOut屬性中),從開始節點到結束節點的每個路徑表明每一個key/value對,在查找一個key時,經過對路徑節點上的邊進行二分查找就能夠快速進行定位。固然當一個節點的邊太多的時候lucene還會進一步優化,是否共享後綴也是可配置的,當不共享後綴而且沒有value時就是一個典型的前綴樹。你們應該瞭解到雙數組trie是對前綴樹的一種優化,實際上FST又未嘗不是呢!less
理解FST並不難,難點在於一個高效的實現,如下是對lucene源碼實現的關鍵性分析,具體細節還請看源碼:優化
一、對於節點node而言分爲序列化的node與未序列化的node,未序列化的node是能夠不斷複用的(當將節點序列化後,node會被重置),每次插入一個key時,首先查找與上一個key的公共前綴,而後將上個key的第lastkey.length個節點到第prefixLen+1個節點開始序列化(因爲通過排序後這些節點之後也不會發生變化所以能夠序列化);網站
二、當序列化完成後會對value值經過查找公共前綴進行進一步壓縮處理,從第2個節點開始一直處理到第prefixLen個節點。實現時將當前值與上一個值的公共前綴設置給當前節點的父節點,將剩餘的後綴推送到當前節點的每一個邊上(當遇到終止節點時,會將後綴保存在節點上一份),而後將當前值與公共前綴的後綴設置爲當前值繼續該循環;ui
三、根節點老是在最後進行序列化。.net
四、在對節點序列化時會將每條邊依次寫入一個相似於ByteBlockPool這樣的邏輯字節流中(其意義可參考談談lucene中的BytesRefHash),首先會寫入元數據(就是上面提到的邊的屬性),而後寫入邊上對應的lab,再寫入value,最後寫入指向節點的地址。注意:這裏不考慮當一個節點的邊太多的時候所作的優化,寫完以後須要將字節序列進行反轉,而後從後向前進行讀取。blog