日常開發工作中,涉及到的數據存儲,要做查詢優化或想深入了解存儲引擎,需要對索引知識有個起碼的了解,下面介紹下最常見的四種索引結構。
-
-
哈希索引
-
BTREE索引
-
倒排索引
1、位圖索引(BitMap)
位圖索引適用於字段值為可枚舉的有限個數值的情況
下圖1 為用戶表,存儲了性別和婚姻狀況兩個字段。
圖2中 分別為性別和婚姻狀態建立了兩個位圖索引;
例如:性別->男對應索引為:101110011,表示第1、3、4、5、8、9個用戶為男性。其他屬性以此類推。

-
男性 並且已婚 的記錄 = 101110011 & 11010010 = 100100010,即第1、4、8個用戶為已婚男性。
-
女性 或者未婚的記錄 = 010001100 | 001010100 = 011011100, 即第2、3、5、6、7個用戶為女性或者未婚。
注:位圖索引查詢主要進行“與/或”操作,性能非常高;並且空間占用少;一個常見的場景就是用着統計標簽化用戶上,對用戶進行分類;Redis提供了方便的位圖操作命令,使用很方便;但位圖“位資源”的回收不方便,且稀松的位圖會浪費空間,位圖進行非運算較困難
BTREE: 有序平衡N叉樹, 每個節點有N個鍵值和N+1個指針, 指向N+1個子節點
一棵BTREE的簡單結構如下圖4所示,為一棵2層的3叉樹,有7條數據:

圖4
以Mysql最常用的InnoDB引擎為例,描述下BTREE索引的應用。

圖5
主鍵索引為圖5的左半部分(如果沒有顯式定義自主主鍵,就用不為空的唯一索引來做聚簇索引,如果也沒有唯一索引,則innodb內部會自動生成6字節的隱藏主鍵來做聚簇索引),葉子節點存儲了完整的數據行信息(以主鍵 + row_data形式存儲)。
二級索引也是以B+tree的形式進行存儲,圖5右半部分,與主鍵不同的是二級索引的葉子節點存儲的不是行數據,而是索引鍵值和對應的主鍵值,由此可以推斷出,二級索引查詢多了一步查找數據主鍵的過程。
維護一顆有序平衡N叉樹,比較復雜的就是當插入節點時節點位置的調整,尤其是插入的節點是隨機無序的情況;而插入有序的節點,節點的調整只發生了整個樹的局部,影響范圍較小,效率較高。
可以參考紅黑樹的節點的插入算法:https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
因此如果innodb表有自增主鍵,則數據寫入是有序寫入的,效率會很高;如果innodb表沒有自增的主鍵,插入隨機的主鍵值,將導致B+tree的大量的變動操作,效率較低。這也是為什么會建議innodb表要有無業務意義的自增主鍵,可以大大提高數據插入效率。
Mysql Innodb使用自增主鍵的插入效率高。
使用類似Snowflake的ID生成算法,生成的ID是趨勢遞增的,插入效率也比較高。
正向索引反映了一篇文檔與文檔中關鍵詞之間的對應關系;給定文檔標識,可以獲取當前文檔的關鍵詞、詞頻以及該詞在文檔中出現的位置信息,如圖6 所示,左側是文檔,右側是索引。

圖6

圖7
圖8

圖9
如圖10,共存在5個文檔,第一列為文檔編號,第二列為文檔的文本內容。

圖10
圖11
-
單詞詞典查詢優化
對於一個規模很大的文檔集合來說,可能包含幾十萬甚至上百萬的不同單詞,能否快速定位某個單詞,這直接影響搜索時的響應速度,其中的優化方案就是為單詞詞典建立索引,有以下幾種方案可供參考:
-
詞典Hash索引
Hash索引簡單直接,查詢某個單詞,通過計算哈希函數,如果哈希表命中則表示存在該數據,否則直接返回空就可以;適合於完全匹配,等值查詢。如圖12,相同hash值的單詞會放在一個沖突表中。

-
類似於Innodb的二級索引,將單詞按照一定的規則排序,生成一個BTree索引,數據節點為指向倒排索引的指針。

圖13
同樣將單詞按照一定的規則排序,建立一個有序單詞數組,在查找時使用二分查找法;二分查找法可以映射為一個有序平衡二叉樹,如圖14這樣的結構。

圖14
4. FST(Finite State Transducers )實現
FST為一種有限狀態轉移機,FST有兩個優點:1)空間占用小。通過對詞典中單詞前綴和后綴的重復利用,壓縮了存儲空間;2)查詢速度快。O(len(str))的查詢時間復雜度。
以插入“cat”、 “deep”、 “do”、 “dog” 、“dogs”這5個單詞為例構建FST(注:必須已排序)。

圖15
當然還有其他的優化方式,如使用Skip List、Trie、Double Array Trie等結構進行優化,不再一一贅述。


