MurmurHash算法

本文介绍了MurmurHash算法,一种非加密型哈希函数,因其速度快、低碰撞和高混淆特性被广泛应用在数据库存储和散列表等场景。文章详细讲解了其工作原理、优点以及在Java中的使用示例,还提到了32位和128位哈希值的选择以及长链接转短链接的应用实例。

MurmurHash:(multiply and rotate) and (multiply and rotate) Hash,乘法和旋转的hash 算法。

一、哈希函数

散列函数(英语:Hash function)又称散列算法、哈希函数,是一种从任何一种数据中创建小的数字“指纹”的方法。

散列函数把消息或数据压缩成摘要,使得数据量变小,将数据的格式固定下来。

该函数将数据打乱混合,重新创建一个叫做散列值(hash values,hash codes)的指纹。散列值通常用一个短的随机字母和数字组成的字符串来代表。好的散列函数在输入域中很少出现散列冲突。

特点:

加密:加密存在数据库中的密码(password)字符串,由于散列算法所计算出来的散列值(Hash Value)具有不可逆(无法逆向演算回原本的数值)的性质,因此可有效的保护密码。

压缩:把任意长度的输入通过散列算法变换成固定长度的输出。

场景:

保护资料、确保传递真实的信息、散列表、错误校正、语音识别、信息安全...

常见哈希算法:

MD系列(MD5)、SHA系列(SHA-1)、CRC,甚至JDK hashCode()也是哈希算法的一种。可以将他们分成三代:

第一代:SHA-1(1993),MD5(1992),CRC(1975),Lookup3(2006)

第二代:MurmurHash(2008)

第三代:CityHash, SpookyHash(2011)

分类可分为加密型、非加密型:

加密型:MD系列(MD5)、SHA系列(SHA-1)

非加密型:CRC、MurmurHash

二、MurmurHash

MurmurHash 是一种非加密型哈希函数,适用于一般的哈希检索操作。由Austin Appleby在2008年发明,并出现了多个变种,都已经发布到了公有领域(public domain)。与其它流行的哈希函数相比,对于规律性较强的key,MurmurHash的随机分布特征表现更良好。

特点:

1.快 ,MurMurHash3 比 MD5 快

2.低碰撞,MurMurHash3 128 位版本哈希值是 128 位的,跟 MD5 一样。128 位的哈希值,在数据量只有千万级别的情况下,基本不用担心碰撞。

3.高混淆,散列值比较“均匀”,如果用于哈希表,布隆过滤器等, 元素就会均匀分布。

广泛应用于各开源产品,Java 界中 Redis,Memcached,Cassandra,Hadoop,HBase,Lucene,spark,nginx,常见的大数据库底层,都使用了这个算法作为底层的存储算法。

MurMurHash3 128 位版本的速度是 MD5 的十倍。有趣的是,MurMurHash3 生成 32 位哈希的用时比生成 128 位哈希的用时要长。原因在于MurMurHash3_128 针对现代 x64 平台cpu进行了优化。

三、MurmurHash的使用

Java版:google guava 包中提供了使用工具类:

<groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
<version>30.1.1-jre</version>
package com.joker.cloud.linserver.conf.murmur;

import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;

import java.nio.charset.StandardCharsets;

/**
 * MurmurHashTest
 *
 * @author joker
 * @version 1.0
 * 2023/3/7 14:29
 **/

public class MurmurHashTest {

    public static void main(String[] args) {
        String base64 = "CSHyrMyg087o3JWW7EWn+llHweWg1OVpxupHegjYREjousvZYdaWMCDWk1nEvDEFpzdsxSBunEPdUlgdu4+lCspuK32t68ruwKCU4KOM8ZIGXjjp10/lMrymjdYYLaIiAhdAHeOfGz+RfYUlJXGn4iV0tahHCGeh9//Ap6Mv6nhxxrbxWwYDnYC6PRvdoMpwaVydfGfValGk+ygZnnr84uAzPytXqGzF23M6gNWtFT29yTMdK3vZaUtkE3AaybRO0DLBkBnqeWXnBNqFQHWnHg==";
        String hash128String = getHexHash128String(base64);
        System.out.println(hash128String);
    }
    
    public static String getHexHash128String(String str) {
        HashFunction hashFunction = Hashing.murmur3_128();
        return hashFunction.hashString(str, StandardCharsets.UTF_8).toString();
    }
}

性能测试:

package com.joker.cloud.linserver.conf.murmur;

import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;

import java.nio.charset.StandardCharsets;

/**
 * MurmurHashTest
 *
 * @author joker
 * @version 1.0
 * 2023/3/7 14:29
 **/

public class MurmurHashTest {

    public static void main(String[] args) {
        String base64 = "CSHyrMyg087o3JWW7EWn+llHweWg1OVpxupHegjYREjousvZYdaWMCDWk1nEvDEFpzdsxSBunEPdUlgdu4+lCspuK32t68ruwKCU4KOM8ZIGXjjp10/lMrymjdYYLaIiAhdAHeOfGz+RfYUlJXGn4iV0tahHCGeh9//Ap6Mv6nhxxrbxWwYDnYC6PRvdoMpwaVydfGfValGk+ygZnnr84uAzPytXqGzF23M6gNWtFT29yTMdK3vZaUtkE3AaybRO0DLBkBnqeWXnBNqFQHWnHg==";
        String hash128String = getHexHash128String(base64);
        System.out.println(hash128String);
        
        
        long l = System.nanoTime();
        int num = 10000000;
        for (int i = 0; i < num; i++) {
            String hexHashString1 = getHexHash128String(base64);
        }
        long time = System.nanoTime() - l;
        System.out.println(num+"条数据,一共花费时间:" + time / (1000 * 1000 * 1000) + "秒");

        long ns = time / (num);
        System.out.println(num+"条数据,每条数据花费时间:" + ns + "纳秒");
        
    }
    
    public static String getHexHash128String(String str) {
        HashFunction hashFunction = Hashing.murmur3_128();
        return hashFunction.hashString(str, StandardCharsets.UTF_8).toString();
    }
}

32位与128位:

MurmurHash 算法提供了两种长度的哈希值,一种是 32bits,一种是 128bits。为了让最终生成的短网址尽可能短,可以选择 32bits 的哈希值。

package com.joker.cloud.linserver.conf.murmur;

import com.google.common.hash.HashFunction;
import com.google.common.hash.Hashing;

import java.nio.charset.StandardCharsets;

/**
 * MurmurHashTest
 *
 * @author joker
 * @version 1.0
 * 2023/3/7 14:29
 **/

public class MurmurHashTest {

    public static String getHexHash32String(String str) {
        HashFunction hashFunction = Hashing.murmur3_32();
        return hashFunction.hashString(str, StandardCharsets.UTF_8).toString();
    }

    public static String getHexHash128String(String str) {
        HashFunction hashFunction = Hashing.murmur3_128();
        return hashFunction.hashString(str, StandardCharsets.UTF_8).toString();
    }

    public static Long getHexHash32Long(String str) {
        HashFunction hashFunction = Hashing.murmur3_32();
        return hashFunction.hashString(str, StandardCharsets.UTF_8).padToLong();
    }
    
}

常用于长链接转短链接:

实现思路是通过哈希算法生成短网址。采用计算速度快、冲突概率小的 MurmurHash 算法,并将计算得到的 10 进制数,转化成 62 进制表示法,进一步缩短短网址的长度。对于哈希算法的哈希冲突问题,通过给原始网址添加特殊前缀字符,重新计算哈希值的方法来解决。

长链接转短链接-优快云博客

MurmurHash 是一种非加密型哈希函数,专为高效哈希检索操作而设计,具有良好的随机分布特性,尤其在处理规律性较强的键值时表现优异。该算法由 Austin Appleby 于 2008 年发明,并发展出多个版本,所有版本均已发布到公有领域,可自由使用[^1]。 ### MurmurHash 的核心原理 MurmurHash 的核心思想是通过一系列位操作和乘法运算来混合输入数据,以生成一个分布均匀的哈希值。以 MurmurHash2 的实现为例,其主要步骤包括: 1. **初始化哈希值**:使用种子值(seed)和输入数据长度的异或操作来初始化哈希值。 2. **分块处理数据**:每次处理 4 字节的数据块,将其转换为一个 32 位整数 `k`,然后通过乘法和位移操作进行混合。 3. **处理剩余字节**:对于不足 4 字节的部分,根据剩余字节数进行单独处理,将这些字节按位插入到哈希值中。 4. **最终混合**:对哈希值进行几次额外的位移和乘法操作,以确保最后的几个字节能够充分融入哈希结果中[^3]。 以下是 MurmurHash2 的核心代码片段: ```c uint32_t MurmurHash2 ( const void * key, int len, uint32_t seed ) { const uint32_t m = 0x5bd1e995; const int r = 24; uint32_t h = seed ^ len; const unsigned char * data = (const unsigned char *)key; while(len >= 4) { uint32_t k = *(uint32_t*)data; k *= m; k ^= k >> r; k *= m; h *= m; h ^= k; data += 4; len -= 4; } switch(len) { case 3: h ^= data[2] << 16; case 2: h ^= data[1] << 8; case 1: h ^= data[0]; h *= m; }; h ^= h >> 13; h *= m; h ^= h >> 15; return h; } ``` ### MurmurHash 的使用场景 MurmurHash 由于其高效的计算速度和良好的分布特性,广泛应用于以下场景: 1. **哈希表**:MurmurHash 适用于构建哈希表,特别是在需要快速查找和插入的场景中。它的均匀分布特性可以减少哈希冲突,提高哈希表的性能。 2. **布隆过滤器(Bloom Filter)**:在布隆过滤器中,MurmurHash 可以用作多个独立哈希函数之一,用于判断一个元素是否可能存在于集合中。 3. **一致性哈希(Consistent Hashing)**:在分布式系统中,MurmurHash 可以用于生成一致性哈希环上的节点位置,从而实现负载均衡。 4. **数据分片(Data Sharding)**:在大数据处理中,MurmurHash 可以用于将数据均匀地分配到不同的分片中,确保数据分布的平衡性。 5. **缓存系统**:MurmurHash 可以用于生成缓存键的哈希值,帮助缓存系统快速定位存储位置。 ### MurmurHash 在实际开发中的应用示例 在实际开发中,MurmurHash 也可以与其他编码方式结合使用,例如将哈希值转换为 62 进制字符串,以生成更短且易于管理的标识符。以下是一个简单的 Java 实现示例,展示了如何使用 MurmurHash 并将其结果转换为 62 进制字符串: ```java import com.google.common.hash.Hashing; public class HashUtil { private static final String CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; public static long murmurHash32(String param) { return Hashing.murmur3_32().hashUnencodedChars(param).padToLong(); } private static String encodeToBase62(long num) { StringBuilder sb = new StringBuilder(); do { int i = (int) (num % 62); sb.append(CHARS.charAt(i)); num /= 62; } while (num > 0); return sb.reverse().toString(); } public static String generateShortHash(String input) { long hash = murmurHash32(input); return encodeToBase62(hash); } } ``` 上述代码中,`murmurHash32` 方法使用了 Guava 库中的 `Hashing.murmur3_32()` 来生成 32 位的 MurmurHash 值,而 `encodeToBase62` 方法则将该哈希值转换为 62 进制字符串,从而生成更短且易于读写的标识符。 ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值