从数组中生成唯一哈希值的最快方法(用于指纹识别)

6
我在我的WWW框架中使用了大量API调用的缓存和缓冲技术,其中一个常用的技巧是“指纹识别”,用于匹配缓存文件名以及检测已经执行过的API调用。
很多数据都是以数组的形式传递,例如GET、POST等等。因此API调用的唯一性取决于这些数据。
于是我需要对这些信息进行指纹识别。为了实现这个目的,需要从数据数组生成一个“指纹”,并将其散列成一个字符串,以便存储和比较。
在PHP中可以使用serialize()和json_encode()对数组进行序列化。在各种基准测试之后,我认为json_encode()是更快的方法,而且我对它非常满意。
关于哈希算法,可以使用md5()和sha1()函数,其中md5()在我的基准测试中速度更快。
因此,我的当前指纹算法如下:
$fingerprint=md5(json_encode($array));

但是我对于这是否是在PHP中指纹识别的“最快可能”方法持怀疑态度。我已经尝试了谷歌和StackOverflow,并没有找到更好的替代方法。我是否走在正确的道路上,还是需要做些不同的事情呢?


2
md5(var_export($data, true)) 有什么比较吗?另外,想知道是否可以依赖于 json_encode 保留顺序。如果键的顺序不同,错过缓存将是很糟糕的事情。 - Hamish
2
根据我的测试,var_export() 比 serialize() 快但比 json_encode() 慢。我正在研究使用 crc32() 替换 md5(),需要进行测试。 - kingmaple
很明显(令我惊讶的是),crc32() 比 md5() 更慢,当然也更容易发生冲突。所以我又回到了之前使用 md5(json_encode($array)) 的地方。 - kingmaple
1
我认为你已经尽可能地压榨了它的性能。你可以考虑只存储/分析GET/POST变量本身,甚至是JSON字符串。我不知道你是否考虑过,如果GET/POST以不同的顺序出现,它将具有完全不同的指纹。 - fie
哦,实际上md5()比hash('md5')稍微快一点,我刚测试过。hash('md4')比它们两个都稍微快一点,但是由于我对md4的经验较少并且存在潜在的问题,所以我现在会继续使用md5()。 - kingmaple
显示剩余2条评论
3个回答

5
一旦您得到了您的数组json_encoded,如果您主要关心速度,那么您应该选择一个非加密哈希函数。不同的哈希函数适用于不同的场景。MD5和Sha1被称为加密方式,因为它们难以反转(请注意,由于漏洞,它们被广泛认为是已过时的安全措施)。CRC(循环冗余校验)函数是错误检测代码,不适合用于唯一性。
维基百科是一个不错的起点,因为那里的贡献通常有对库实现的外部链接:哈希函数列表。我建议阅读其中几个非加密库,并对它们进行基准测试。非加密函数更多地是为了速度和合理程度的唯一性而编写,牺牲了安全性、错误检测和其他有趣的属性,而这正是您所需要的。
最后一个需要考虑的问题是,如果您主要关心速度,那么您将如何存储和比较指纹本身。MD5输出128位数据,这些数据无法在php中的数字类型中适合,需要额外的库调用和开销。我敢打赌,您可以通过使用直接输出64位数字的哈希函数来获得最佳的比较和存储速度。请注意,要在php中本地获得64位数字,您需要拥有64位硬件并以64位模式配置/安装php。如果您感兴趣,我有一些代码可以测试我们的预备和生产环境。
顺便说一句,我认为您不可能获得比json-encode更快的数组字符串化方法。问题的核心是数组遍历和字符串操作,因此速度与输出的详细程度成正比。与php的序列化或导出函数相比,JSON-encode非常简洁。我敢打赌,如果您浏览了足够的php文档页面评论,您就可以找到一个以数组作为输入的哈希函数,但这是一个赌注,它是否真的好用。
如果我有任何不清楚的地方,请随时提问。

虽然这不是最好的答案,但考虑到情况,已经足够了。感谢您的帮助! - kingmaple
3
考虑到其独特性,我会说这是最好的答案。 - tiwo

4

我在100个循环中测试了7000个数组的各种方法,最快的是crc32(serialize($arr))(平均0.65秒)。

    $arr = ['name' => $name, 'category' => $category, 'hits' => $hits];

    md5(var_export($arr, true));
    md5(serialize($arr));
    md5(json_encode($arr));

    hash('md5', var_export($arr, true), "");
    hash('md5', serialize($arr), "");
    hash('md5', json_encode($arr), "");

    hash('md4', var_export($arr, true), "");
    hash('md4', serialize($arr), "");
    hash('md4', json_encode($arr), "");

    crc32(var_export($arr, true));
    crc32(serialize($arr));
    crc32(json_encode($arr));

    sha1(var_export($arr, true));
    sha1(serialize($arr));
    sha1(json_encode($arr));

    spl_object_hash((object) $arr);

1
我认为@Patrick M的解释非常好,我只想在这里添加一些额外的数据。
正如@Patrick M所说,md5是一种加密哈希算法,即使现在已经被弃用,它仍会增加一些额外的安全性。
您可以使用hash_algos函数检查可用的哈希函数。
但是,在这里您可以看到这些算法的基准测试,其中它说(我自己没有尝试)最快的是md4,然后是md5等。
为了公正起见,基准测试应该使用特定的函数(如md5sha1crc32等),以确保它可能更快(或更慢,谁知道)。
总之,我认为你的方法看起来相当不错。只需记住md5不安全,因为它在PHP密码哈希FAQ中有所描述,所以如果您需要存储信用卡指纹或类似物品(我不认为这是您的情况),您可能需要另一个函数以及添加一些额外步骤。
作为替代方案,您可以检查spl_object_hash

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接