数据库伪哈希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,生成的时候用这个。
在这里插入图片描述

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