常見索引介紹


日常開發工作中,涉及到的數據存儲,要做查詢優化或想深入了解存儲引擎,需要對索引知識有個起碼的了解,下面介紹下最常見的四種索引結構。

  1. 位圖索引

  2. 哈希索引

  3. BTREE索引

  4. 倒排索引

1、位圖索引(BitMap)

位圖索引適用於字段值為可枚舉的有限個數值的情況

位圖索引使用二進制的數字串(bitMap)標識數據是否存在,1標識當前位置(序號)存在數據,0則表示當前位置沒有數據。

​下圖1 為用戶表,存儲了性別婚姻狀況兩個字段。

​圖2中 分別為性別婚姻狀態建立了兩個位圖索引;

例如:性別->男對應索引為:101110011,表示第1、3、4、5、8、9個用戶為男性。其他屬性以此類推。

 

使用位圖索引查詢:

  • 男性 並且已婚 的記錄 = 101110011 & 11010010 = 100100010,即第1、4、8個用戶為已婚男性。

  • 女性 或者未婚的記錄 = 010001100 | 001010100 = 011011100, 即第2、3、5、6、7個用戶為女性或者未婚。

注:位圖索引查詢主要進行“與/或”操作,性能非常高;並且空間占用少;一個常見的場景就是用着統計標簽化用戶上,對用戶進行分類;Redis提供了方便的位圖操作命令,使用很方便;但位圖“位資源”的回收不方便,且稀松的位圖會浪費空間,位圖進行非運算較困難

 

2、哈希索引

顧名思義,是指使用某種哈希函數實現key->value 映射的索引結構。

哈希索引適用於等值檢索,通過一次哈希計算即可定位數據的位置。

下圖3 展示了哈希索引的結構,與JAVA中HashMap的實現類似,是用沖突表的方式解決哈希沖突的。

 

圖3

 

3、BTREE索引

BTREE索引是關系型數據庫最常用的索引結構,方便了數據的查詢操作。

BTREE: 有序平衡N叉樹, 每個節點有N個鍵值和N+1個指針, 指向N+1個子節點

一棵BTREE的簡單結構如下圖4所示,為一棵2層的3叉樹,有7條數據:

 

圖4

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

 

圖5

Innodb下的表都是以索引組織表形式存儲的,也就是整個數據表的存儲都是B+tree結構的,如圖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是趨勢遞增的,插入效率也比較高。

 

4、倒排索引(反向索引)

​倒排索引也叫反向索引,可以相對於正向索引進行比較理解。

正向索引反映了一篇文檔文檔中關鍵詞之間的對應關系;給定文檔標識,可以獲取當前文檔的關鍵詞、詞頻以及該詞在文檔中出現的位置信息,如圖6 所示,左側是文檔,右側是索引。

圖6

反向索引則是指某關鍵詞該詞所在的文檔之間的對應關系;給定了關鍵詞標識,可以獲取關鍵詞所在的所有文檔列表,同時包含詞頻、位置等信息,如圖7所示。

 

 圖7

​ 反向索引(倒排索引)的單詞的集合和文檔的集合就組成了如圖8所示的”單詞-文檔矩陣“,打鈎的單元格表示存在該單詞和文檔的映射關系。

圖8

倒排索引的存儲結構可以參考圖9。其中詞典是存放的內存里的,詞典就是整個文檔集合中解析出的所有單詞的列表集合;每個單詞又指向了其對應的倒排列表,倒排列表的集合組成了倒排文件,倒排文件存放在磁盤上,其中的倒排列表內記錄了對應單詞在文檔中信息,即前面提到的詞頻、位置等信息。

圖9

下面以一個具體的例子來描述下,如何從一個文檔集合中生成倒排索引。

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

 

圖10

將上述文檔集合進行分詞解析,其中發現的10個單詞為:[谷歌,地圖,之父,跳槽,Facebook,加盟,創始人,拉斯,離開,與],以第一個單詞”谷歌“為例:首先為其賦予一個唯一標識 ”單詞ID“, 值為1,統計出文檔頻率為5,即5個文檔都有出現,除了在第3個文檔中出現2次外,其余文檔都出現一次,於是就有了圖11所示的倒排索引。

 

圖11

  • 單詞詞典查詢優化

​ 對於一個規模很大的文檔集合來說,可能包含幾十萬甚至上百萬的不同單詞,能否快速定位某個單詞,這直接影響搜索時的響應速度,其中的優化方案就是為單詞詞典建立索引,有以下幾種方案可供參考:

  1. 詞典Hash索引

    Hash索引簡單直接,查詢某個單詞,通過計算哈希函數,如果哈希表命中則表示存在該數據,否則直接返回空就可以;適合於完全匹配,等值查詢。如圖12,相同hash值的單詞會放在一個沖突表中。

 

圖12

  1. 詞典BTREE索引

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

 

圖13

3. 二分查找

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

 

 

圖14

 

4. FST(Finite State Transducers )實現

FST為一種有限狀態轉移機,FST有兩個優點:1)空間占用小。通過對詞典中單詞前綴和后綴的重復利用,壓縮了存儲空間;2)查詢速度快。O(len(str))的查詢時間復雜度。

以插入“cat”、 “deep”、 “do”、 “dog” 、“dogs”這5個單詞為例構建FST(注:必須已排序)。

 

圖15

如圖15 最終我們得到了如上一個有向無環圖。利用該結構可以很方便的進行查詢,如給定一個詞 “dog”,我們可以通過上述結構很方便的查詢存不存在,甚至我們在構建過程中可以將單詞與某一數字、單詞進行關聯,從而實現key-value的映射。

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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM