MySQL中的hash index

2020-09-21 22:23:30

題外話

前幾天有人問到MongoDB的hash分片是怎麼實現的,當時記憶有些模糊,聯想到了mysql的hash索引,故簡單整理了此文。MongoDB的hash分片介紹可參考我另一文章:https://blog.csdn.net/tah_001/article/details/108690215

一、雜湊表(雜湊表)

雜湊表(Hash table,也叫雜湊表),是依據關鍵碼值(Key value)而直接進行存取的資料結構。也就是說,它通過把關鍵碼值對映到表中一個位置來存取記錄,以加快查詢的速度。這個對映函數叫做雜湊函數,存放記錄的陣列叫做雜湊表。

雜湊表,它是基於高速存取的角度設計的,也是一種典型的「空間換時間」的做法。顧名思義,該資料結構能夠理解為一個線性表,可是當中的元素不是緊密排列的,而是可能存在空隙。

雜湊方法的主要思想是根據結點的關鍵碼值來確定其儲存地址:以關鍵碼值K為自變數,通過一定的函數關係h(K)(稱為雜湊函數),計算出對應的函數值來,把這個值解釋為結點的儲存地址,將結點存入到此儲存單元中。檢索時,用同樣的方法計算地址,然後到相應的單元裡去取要找的結點。通過雜湊方法可以對結點進行快速檢索。雜湊(hash,也稱「雜湊」)是一種重要的儲存方式,也是一種常見的檢索方法。

二、Hash 索引

雜湊索引(hash index)基於雜湊表實現,只有精確匹配索引所有列的查詢才有效,對於每一行資料,儲存引擎都會對所有的索引列計算一個雜湊碼,雜湊碼是一個較小的值,並且不同鍵值的行計算出來的雜湊碼也不一樣。雜湊碼索引將所有的雜湊碼儲存在索引中,同時在雜湊表中儲存指向每個資料行的指標。

因為索引自身只需儲存對應的雜湊值,所以索引的結構十分緊湊,這也讓雜湊索引查詢的速度非常快。然而,雜湊索引也有他的限制:

1)雜湊索引只包含雜湊值和行指標,而不儲存欄位值,所以不能使用索引中的值來避免讀取行,不過,存取記憶體中的行的速度很快,所以大部分情況下這一點對效能的影響並不明顯。

2)雜湊索引資料並不是按照索引值順序儲存的,所以也就無法用於排序

3)雜湊索引也不支援部分索引列匹配查詢,因為雜湊索引始終是使用索引列的全部內容來計算雜湊值的。

4)雜湊索引只支援等值比較查詢,包括=、IN()、<=>、也不支援任何範圍查詢。

5)存取雜湊索引的資料非常快,除非有很多雜湊衝突(不同的索引列值卻有相同的雜湊值)。當出現雜湊衝突的時候,儲存引擎必須遍歷連結串列中所有的行指標,逐行進行比較,直到找到所有符合條件的行。

6)如果雜湊衝突很多的話,一些索引維護操作的代價也會很高。例如,如果在某個選擇性很低(雜湊衝突很多)的列上建立雜湊索引,那麼當從表中刪除一行時,儲存引擎需要遍歷對應雜湊值的連結串列中的每一行,找到並刪除對應的參照,衝突越多,代價越大。 

因為這些限制,雜湊索引只適用於某些特定的場合。而一旦適合雜湊索引,則它帶來的效能提升將非常顯著。舉個例子,在資料倉儲應用中有一種經典的「星型」 schema,需要關聯很多查詢表,雜湊索引就非常適合查詢表的需求。

除了Memory引擎外,NDB叢集引擎也支援唯一雜湊索引,且在NDB叢集引擎中作用非常特殊。

三、InnoDB 中的自適應雜湊索引

InnoDB 引擎有一個特殊額功能叫做「自適應雜湊索引」,當 InnoDB注意到某些索引值被使用得非常頻繁時,它會在記憶體中基於B-Tree索引之上再建立一個雜湊索引,這樣就讓B-Tree索引頁具有雜湊索引的一些優點,比如快速的雜湊查詢。這是一個完全自動的、內部的行為,使用者無法控制或者設定,不過若果有必要,完全可以關閉該功能。

四、總結

上文可以知道hash index的效率非常高,但在mysql中限制也很多,所以我們應按實際情況合理使用;最後借鑑一匿名網友生動的描述,理解一下hash演演算法:(借鑑地址:https://zhidao.baidu.com/question/548095906.html)

假如我們有很多的小豬,每個4102的體重都不一樣,假設體重分佈比較平均(我們考慮到公斤級別),我們按照體重來分,劃分成100個小豬圈。
然後把每個小豬,按照體重趕進各自的豬圈裡,記錄檔案。 好了,如果我們要找某個小豬怎麼辦呢?我們需要每個豬圈,每個小豬的比對嗎?
當然不需要了。 我們先看看要找的這個小豬的體重,然後就找到了對應的豬圈了。
在這個豬圈裡的小豬的數量就相對很少了。
我們在這個豬圈裡就可以相對快的找到我們要找到的那個小豬了。 對應於hash演演算法。
就是按照hashcode分配不同的豬圈,將hashcode相同的豬放到一個豬圈裡。
查詢的時候,先找到hashcode對應的豬圈,然後在逐個比較裡面的小豬。 所以問題的關鍵就是建造多少個豬圈比較合適。 如果每個小豬的體重全部不同(考慮到毫克級別),每個都建一個豬圈,那麼我們可以最快速度的找到這頭豬。缺點就是,建造那麼多豬圈的費用有點太高了。 如果我們按照10公斤級別進行劃分,那麼建造的豬圈只有幾個吧,那麼每個圈裡的小豬就很多了。我1653們雖然可以很快的找到豬圈,但從這個豬圈裡逐個確定那頭小豬也是很累的。 所以,好的hashcode,可以根據實際情況,根據具體的需求,在時間成本(更多的豬圈,更快的速度)和空間本(更少的豬圈,更低的空間需求)之間平衡。

哎喲,不錯噢! - - - - - - 歡迎指出有誤的地方以及補充更好的方法