【问题标题】:Fastest method for fingerprinting an array (calculating a unique hash from an array of data)对数组进行指纹识别的最快方法(从数据数组计算唯一哈希)
【发布时间】:2012-04-06 11:29:18
【问题描述】:

我在我的 WWW 框架中使用了大量的 API 调用缓存和缓冲,而我最终使用的其中一件事是“指纹”数据以匹配缓存文件名以及检测具有已经制作好了。

大量数据在数组中移动,例如 GET、POST 等。因此,API 调用的唯一性取决于数据。

因此,我需要对这些信息进行指纹识别。要做到这一点,还需要从数据数组中生成一个“指纹”,并将其散列成一个我可以存储和比较的字符串。

对于数组序列化,PHP 中有 serialize() 和 json_encode()。经过各种基准测试后,我认为 json_encode() 是序列化数组的更快方法,并且对它非常满意。

对于散列,有 md5() 和 sha1() 函数,根据我的基准,其中 md5() 更快。

所以我目前的指纹算法是:

$fingerprint=md5(json_encode($array));

但我怀疑这是否是在 PHP 中对数组进行指纹识别的“最快”方法。我已经尝试过 Google 和 StackOverflow,但还没有找到好的替代品。我是在正确的轨道上还是我需要做一些不同的事情?

【问题讨论】:

  • md5(var_export($data, true)) 比较如何?另外 - 想知道您是否可以依靠json_encode 来保存订单。错过缓存会很糟糕,因为键顺序不同。
  • 根据我的测试, var_export() 比 serialize() 快,但比 json_encode() 慢。我正在研究 crc32() 来替换 md5(),需要测试。
  • 好吧,显然(令我惊讶的是) crc32() 实际上比 md5() 慢,当然更容易发生可能的冲突。所以我又回到了以前的 md5(json_encode($array));
  • 我认为你已经尽可能多地挤出了它。您可能会考虑只存储/分析 GET/POST 变量本身,甚至是 JSON 字符串。我不知道你是否考虑过,但如果 GET/POST 的顺序不同,它的指纹就会完全不同。
  • 另外,我听说 hash('md5',"") 更快,而 hash('md4',"") 甚至比这更快。不过,您必须在自己的系统上对其进行测试。

标签: php arrays hash fingerprint


【解决方案1】:

一旦你得到你的数组json_encoded,如果你主要关心速度,你可能应该使用非密码哈希函数。不同的哈希函数适用于不同的事物。 MD5 和 Sha1 被称为加密,因为它们难以逆转(请注意,由于存在漏洞,出于安全目的,它们被广泛认为已弃用)。 CRC(循环冗余校验)函数是错误检测代码,无论如何都不适合唯一性。

Wikipedia 是一个不错的起点,因为那里的贡献通常具有指向库实现的外部链接:List of hash functions。我建议阅读那里的一些非加密库并对其进行基准测试。非加密函数更多是为了速度和合理程度的唯一性而编写的,牺牲了安全性、错误检测和其他有趣的属性,从您的描述来看,这正是您想要的。

如果您主要关心速度,需要考虑的最后一点是您将如何存储和比较指纹本身。 MD5 输出 128 位数据,如果没有一些额外的库调用和开销,这些数据将不适合 php 中的数字类型。以我的钱,我敢打赌,您可以获得最佳的比较速度,并且存储将来自可以直接输出 64 位数字的哈希函数。请注意,要在 php 中本地获取 64 个数字,您需要拥有 64 位硬件并在 64 位模式下配置/安装 php。我在这里有一些代码,用于测试我们的暂存和生产环境,如果您有兴趣,我可能会挖掘出来。

顺便说一句,我认为您不会获得比 json-encode 更快的数组字符串化。该问题的核心是数组遍历和字符串操作,因此速度基本上与输出的详细程度成正比。与 php 的序列化或导出功能相比,JSON 编码非常简洁。我敢打赌,如果您在 php 文档页面上浏览了足够多的 cmets,您会发现有人编写了一个直接将数组作为输入的哈希函数,但它是否有任何好处都将是一场赌博。

如果我有任何不清楚的地方,请随时提出问题。

【讨论】:

  • 不是最好的答案,但考虑到情况就足够了。感谢您的帮助!
  • 其中一种情况是它的独特性,我会说它最好的答案。
【解决方案2】:

我在 7000 个阵列上测试了 100 个循环中的各种方法,最快的是 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);

【讨论】:

    【解决方案3】:

    我觉得@Patrick M的解释真的很好,我只是想在这里补充一些数据。

    正如@Patrick M 所说,md5 是一个加密哈希,它将做出一些额外的努力来增加安全性(即使现在已为此目的而弃用)。

    您可以使用hash_algos 函数检查您有哪些hash 函数可用。

    但是,在这里你可以看到benchmark of those algorithms,它说(我自己没试过)更快的是md4,然后是md5,等等。

    公平地说,基准测试也应该使用特定函数,如果有的话,可以肯定(md5sha1crc32,...),因为它可能更快(或更慢,谁知道)。

    总之,我认为您的方法看起来不错。

    请记住,md5 并不安全,正如PHP's Password Hashing FAQ 中所述,因此如果您需要存储信用卡指纹或其他类似的东西(我认为这不是您的情况),您可能需要其他功能以及添加一些额外的步骤。

    您也可以查看spl_object_hash

    【讨论】:

      猜你喜欢
      • 2019-02-27
      • 1970-01-01
      • 2010-09-27
      • 2011-11-24
      • 1970-01-01
      • 1970-01-01
      • 2014-09-15
      • 1970-01-01
      • 2018-10-23
      相关资源
      最近更新 更多