數據庫僞哈希Hash索引的創建和使用(理論+實戰)

適用場景

適用於給一個varchar類型的字段建立索引。比如說類的全路徑,URL等長字符串的字段。
com.xxx.aaa.bbb.ccc.yyy.ZZZ

其中大寫的ZZZ就是類名。

像這種長字符串的,建立前綴索引區分度也不大,尤其一般前綴索引只使用10個字符,那麼區分度可以說非常小。
所以,這種情況下,就可以考慮使用僞哈希索引。我們可以創建一個短的帶有 b+tree索引的字段。

innodb也支持hash索引的,但是我們必須啓用(也就是說通常情況下是不啓用的),開啓後,hash索引的創建由InnoDB存儲引擎引擎自動優化創建,我們干預不了

索引的使用原則

索引字段儘量使用數字型(簡單的數據類型)
若只含數值信息的字段儘量不要設計爲字符型,這會降低查詢和連接的性能,並會增加存儲開銷.這是因爲引擎在處理查詢和連接時會逐個比較字符串中每一個字符,而對於數字型而言只需要比較一次就夠了

儘量不要讓字段的默認值爲NULL
在MySQL中,含有空值的列很難進行查詢優化,因爲它們使得索引、索引的統計信息以及比較運算更加複雜.

創建僞hash索引的字段並創建組合索引

因爲我目前有一張表,有個應用ID,類路徑的字段,還有方法名的字段,然後業務需要根據此3個字段進行group by操作來區分唯一性,以及查詢時候使用。所以,首先需要建立一個組合索引。
根據我們上面的原則:

  • 1、應用id是數字型的沒問題,可以建立索引
  • 2、關於類的全路徑字段,需要新增一個僞hash索引字段
ALTER TABLE mytable add clspath_hash int(10) NOT NULL COMMENT '類路徑的僞哈希索引字段';
  • 3、建立組合索引,關於方法名,也建立一個僞hash索引
ALTER sqlTABLE mytable add methname_hash int(10) NOT NULL COMMENT '方法名的僞哈希索引字段';

4、建立組合索引

ALTER TABLE mytable ADD INDEX appid_clp_methname (application_id,clspath_hash,method_name(10));
生成hash值

目前hash算法非常多,Java內部Object類有native的,string有自己的,hashmap在對象hashcode基礎上又進行了一次hash,保證質量。

object

     * @return  a hash code value for this object.
     * @see     java.lang.Object#equals(java.lang.Object)
     * @see     java.lang.System#identityHashCode
     */
    public native int hashCode();

hashmap

   static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

從上面的代碼可以看到key的hash值的計算方法。key的hash值高16位不變,低16位與高16位異或作爲key的最終hash值。(h >>> 16,表示無符號右移16位,高位補0,任何數跟0異或都是其本身,因此key的hash值高16位不變。)
在這裏插入圖片描述
爲什麼要這麼幹呢?
這個與HashMap中table下標的計算有關。

n = table.length;
index = (n-1) & hash;
因爲,table的長度都是2的m次冪,因此index僅與hash值的低m位有關,hash值的高位都被與操作置爲0了。
假設table.length=2^4=16。

在這裏插入圖片描述
由上圖可以看到,只有hash值的低4位參與了運算。
這樣做很容易產生碰撞。設計者權衡了speed, utility, and quality,將高16位與低16位異或來減少這種影響。設計者考慮到現在的hashCode分佈的已經很不錯了,而且當發生較大碰撞時也用樹形存儲降低了衝突。僅僅異或一下,既減少了系統的開銷,也不會造成的因爲高位沒有參與下標的計算(table長度比較小時),從而引起的碰撞。

我這裏使用我的心頭好,murmur3 hash方法

MurMurHash3 哈希算法是 MurMurHash 算法家族的最新一員。雖說是“最新一員”,但距今也有五年的歷史了。無論從運算速度、結果碰撞率,還是結果的分佈均衡程度進行評估,MurMurHash3 都算得上一個優秀的哈希算法。
除了 128 位版本以外,它還有生成 32 位哈希值的版本。在某些場景下,比如哈希的對象長度小於 128 位,或者存儲空間要求佔用小,或者需要把字符串轉換成一個整數,這一特性就能幫上忙。當然,32 位哈希值發生碰撞的可能性就比 128 位的要高得多。當數據量達到十萬時,就很有可能發生碰撞。
https://github.com/spacewander/lua-resty-murmurhash3/blob/master/README.md#when-should-i-use-it
可以看到,MurMurHash3 128 位版本的速度是 MD5 的十倍。有趣的是,MurMurHash3 生成 32 位哈希的用時比生成 128 位哈希的用時要長。原因在於生成 128 位哈希的實現受益於現代處理器的特性。
在這裏插入圖片描述
在這裏插入圖片描述
說明下:因爲我這邊對性能不是要求很高,且更珍惜存儲空間,所以選擇了32位的。另外這個murmurhash的依賴lucence,redis,以及google的guava的Hashing類裏都有使用。鑑於我項目中有redis,懶的引其他依賴了,於是就用了redis裏面的murmurhash依賴。

關於hash seed,生成的時候用這個。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章