测试哪一个HashStr函数最适合你

背景

string hash函数在分布式设计中使用非常的频繁。比如数据库分表,需要根据数据库的主键进行hash,比如负载均衡,需要根据ip或url或uri进行hash。但是根据不同的给定的键,哪种hashStr函数的冲突率最低,执行速度最快呢?有没有一种最优的hashStr函数呢?

HashStr函数

下面是引用别人的帖子,是对字符串hash函数的比较,不再赘述。
各种字符串Hash函数比较
A Hash Function for Hash Table Lookup

在信息修竞赛中,要本着易于编码调试的原则,个人认为BKDRHash是最适合记忆和使用的。 —— BYVoid

是不是如作者所说,试一试才知道。

试验

使用go语言写了一个test,用来测试给定的key集,使用不同的hashStr函数进行测试,看看最后的分布情况,执行效率。
git地址:https://github.com/wllenyj/TestHashStr

我的使用场景

我的工作项目中有一些id,是20多位数字组成。在程序处理过程中需要对id map进行上锁。数量大了以后,为了增加处理的并发量,对id分散到16/32个map再分别使用16/32个锁。

测试结果

取了28k的数据进行测试,hash函数找了一些经典实现,还从github上找了一些好的实现。

$ sh run.sh 
=== RUN   TestBKDRHash1_16
--- PASS: TestBKDRHash1_16 (0.00s)
        hash_test.go:57: [1678 1659 1627 1711 1686 1690 1658 1930 1690 1712 1907 2024 1697 1982 1785 1739]
=== RUN   TestBKDRHash1_32
--- PASS: TestBKDRHash1_32 (0.00s)
        hash_test.go:57: [863 849 830 837 865 854 842 859 904 792 882 917 841 869 958 864 815 810 797 874 821 836 816 1071 786 920 1025 1107 856 1113 827 875]
=== RUN   TestBKDRHash2_16
--- PASS: TestBKDRHash2_16 (0.00s)
        hash_test.go:57: [1638 1674 1657 1987 1722 1815 1692 1752 1710 1710 1722 1970 1681 1844 1906 1695]
=== RUN   TestBKDRHash2_32
--- PASS: TestBKDRHash2_32 (0.00s)
        hash_test.go:57: [809 857 857 852 854 824 856 882 795 857 813 891 812 814 1065 827 829 817 800 1135 868 991 836 870 915 853 909 1079 869 1030 841 868]
=== RUN   TestBKDRHash3_16
--- PASS: TestBKDRHash3_16 (0.00s)
        hash_test.go:57: [1638 1674 1657 1987 1722 1815 1692 1752 1710 1710 1722 1970 1681 1844 1906 1695]
=== RUN   TestBKDRHash3_32
--- PASS: TestBKDRHash3_32 (0.00s)
        hash_test.go:57: [807 850 823 1072 846 924 854 889 814 895 909 915 899 818 850 852 831 824 834 915 876 891 838 863 896 815 813 1055 782 1026 1056 843]
=== RUN   TestSDBMHash_16
--- PASS: TestSDBMHash_16 (0.00s)
        hash_test.go:57: [1621 1715 1848 1673 1701 1689 1766 1764 1747 1656 1686 2062 1682 1983 1677 1905]
=== RUN   TestSDBMHash_32
--- PASS: TestSDBMHash_32 (0.00s)
        hash_test.go:57: [545 528 665 400 360 365 462 401 482 467 573 671 701 851 916 1186 1076 1187 1183 1273 1341 1324 1304 1363 1265 1189 1113 1391 981 1132 761 719]
=== RUN   TestAPHash_16
--- PASS: TestAPHash_16 (0.01s)
        hash_test.go:57: [1854 2079 1740 1682 1837 1649 1698 1661 1686 1708 1639 1639 1773 1780 1801 1949]
=== RUN   TestAPHash_32
--- PASS: TestAPHash_32 (0.01s)
        hash_test.go:57: [834 897 928 845 1049 843 823 823 845 852 848 845 844 909 960 817 1020 1182 812 837 788 806 875 838 841 856 791 794 929 871 841 1132]
=== RUN   TestMetroHash_16
--- PASS: TestMetroHash_16 (0.00s)
        hash_test.go:57: [1901 1714 1670 1664 1974 1733 1716 1970 1685 1709 1706 1693 1829 1716 1762 1733]
=== RUN   TestMetroHash_32
--- PASS: TestMetroHash_32 (0.00s)
        hash_test.go:57: [1066 885 829 849 1124 841 862 1125 879 821 863 841 792 863 855 837 835 829 841 815 850 892 854 845 806 888 843 852 1037 853 907 896]
=== RUN   TestJavaHash_16
--- PASS: TestJavaHash_16 (0.01s)
        hash_test.go:57: [1621 1715 1848 1673 1701 1689 1766 1764 1747 1656 1686 2062 1682 1983 1677 1905]
=== RUN   TestJavaHash_32
--- PASS: TestJavaHash_32 (0.01s)
        hash_test.go:57: [545 528 665 400 360 365 462 401 482 467 573 671 701 851 916 1186 1076 1187 1183 1273 1341 1324 1304 1363 1265 1189 1113 1391 981 1132 761 719]
=== RUN   TestDJBHash_16
--- PASS: TestDJBHash_16 (0.01s)
        hash_test.go:57: [2033 1670 1897 1718 1850 1841 1712 1683 1710 1637 1688 1666 1686 1872 1871 1641]
=== RUN   TestDJBHash_32
--- PASS: TestDJBHash_32 (0.01s)
        hash_test.go:57: [1639 1326 1599 1395 1421 1377 1297 1207 1165 1020 977 865 760 610 737 478 394 344 298 323 429 464 415 476 545 617 711 801 926 1262 1134 1163]
=== RUN   TestSuperFastHash_16
--- PASS: TestSuperFastHash_16 (0.01s)
        hash_test.go:57: [1637 1692 1664 1664 1911 1652 1697 1980 1734 1972 1776 1692 1753 1678 1906 1767]
=== RUN   TestSuperFastHash_32
--- PASS: TestSuperFastHash_32 (0.01s)
        hash_test.go:57: [794 800 844 871 1063 796 860 1155 891 844 919 848 806 835 1086 899 843 892 820 793 848 856 837 825 843 1128 857 844 947 843 820 868]
PASS
ok      _/.../hash/hashstr    0.121s
goos: linux
goarch: amd64
BenchmarkBKDRHash1-4                 500           3207708 ns/op
BenchmarkBKDRHash2-4                 500           3239999 ns/op
BenchmarkBKDRHash3-4                 500           3283682 ns/op
BenchmarkSDBMHash-4                  300           4476090 ns/op
BenchmarkAPHash-4                    200           6059254 ns/op
BenchmarkMetroHash-4                2000           1145394 ns/op
BenchmarkJavaHash-4                  200           8656975 ns/op
BenchmarkDJBHash-4                   200           8649133 ns/op
BenchmarkSuperFastHash-4             200           9277220 ns/op
PASS
ok      _/.../hash/hashstr    20.046s
  • 冲突率:除了个别表现不好的,基本都差不多。
  • 执行效率:MetroHash明显优与其他,但是实现复杂。

结论

如上面帖子作者BYVoid所说,在信息竞赛中,为了好记忆使用BKDRHash就足够了,实现简单,效率也不差,冲突率控制好primeRK。
如果在项目中使用的话,MetroHash无可挑剔。

引用

引用的Hash函数库:
github.com/dgryski/dgohash
github.com/dgryski/go-metro
作者对hash函数很有研究,有兴趣可以去看看作者的github。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是使用Java语言编写的数据通过MD5加盐加密的Demo: ```java import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; public class Md5SaltDemo { public static void main(String[] args) throws NoSuchAlgorithmException { // 需要加密的数据 String data = "hello world"; // 生成随机盐值 byte[] salt = new byte[16]; SecureRandom random = new SecureRandom(); random.nextBytes(salt); // 将数据和盐值拼接,并计算 MD5 值 MessageDigest md5 = MessageDigest.getInstance("MD5"); md5.update((data + new String(salt)).getBytes()); byte[] digest = md5.digest(); // 将计算出的 MD5 值转换为十六进制字符串 StringBuilder sb = new StringBuilder(); for (byte b : digest) { sb.append(String.format("%02x", b)); } String hashStr = sb.toString(); System.out.println("盐值:" + bytesToHex(salt)); System.out.println("加密后的数据:" + hashStr); } private static final char[] HEX_ARRAY = "0123456789abcdef".toCharArray(); public static String bytesToHex(byte[] bytes) { char[] hexChars = new char[bytes.length * 2]; for (int i = 0; i < bytes.length; i++) { int v = bytes[i] & 0xFF; hexChars[i * 2] = HEX_ARRAY[v >>> 4]; hexChars[i * 2 + 1] = HEX_ARRAY[v & 0x0F]; } return new String(hexChars); } } ``` 运行上面的代码,输出结果如下: ``` 盐值:ec3a2c1c8b7b7a01e1a4d3e3f6f4e5c1 加密后的数据:a5c4bbb0c9d8c497544d8a2b9dcf0c0f ``` 以上代码生成了一个长度为 16 字节的随机盐值,将数据和盐值拼接成新的字符串,再计算该字符串的 MD5 值,并输出盐值和加密后的数据。由于盐值是随机生成的,因此每次执行结果都会不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值