如何加快汉明距离的BIT_COUNT查询


How do I speed up this BIT_COUNT query for hamming distance?

>我有一个php脚本,可以检查从安全摄像头拍摄的2张静态照片之间的汉明距离。

该表是具有 2.4M 行的 mySQL,由一个密钥和 4 个 INT(10) 组成。INT(10) 已经单独、一起和与密钥一起索引,但我没有重要的证据表明任何组合都比其他组合更快。如果您建议,我可以再试一次。

汉明权重是通过将图像转换为 8x16 像素来计算的,并且每个季度的位存储在一列中,pHash0、pHash1...等。

我有两种写法。第一种方法是使用嵌套派生表。从理论上讲,每个推导要检查的数据应该比其前身少。查询是预准备语句,并且 ?字段是我正在检查的文件的 pHash[0-3]。

Select
    `Key`,
    Bit_Count(T3.pHash3 ^ ?) + T3.BC2 As BC3
  From
    (Select
      *,
      Bit_Count(T2.pHash2 ^ ?) + T2.BC1 As BC2
    From
      (Select
        *,
        Bit_Count(T1.pHash1 ^ ?) + T1.BC0 As BC1
      From
        (Select
          `Key`,
          pHash0,
          pHash1,
          pHash2,
          pHash3,
          Bit_Count(pHash0 ^ ?) As BC0
        From
          files
        Where
          Not pHash0 Is Null And
          Bit_Count(pHash0 ^ ?) < 4) As T1
      Where
        Bit_Count(T1.pHash1 ^ ?) + T1.BC0 < 4) As T2
    Where
      Bit_Count(T2.pHash2 ^ ?) + T2.BC1 < 4) As T3
  Where
    Bit_Count(T3.pHash3 ^ ?) + T3.BC2 < 4

第二种方法更直接一些。它只是一次完成了所有工作。

Select
    `Key`,
  From
    files
  Where
    Not pHash0 is null AND
    Bit_Count(pHash0 ^ ?) + Bit_Count(pHash1 ^ ?) + Bit_Count(pHash2 ^
    ?) + Bit_Count(pHash3 ^ ?) < 4
第一个查询在大型记录集上更快,

而第二个查询在较小的记录集上更快,但在 2.4M 记录上,每次比较都不会超过 1-1/3 秒。

您是否看到一种调整此过程以使其更快的方法?可以快速尝试任何建议,例如更改数据类型或索引。

设置是Win7x64,MySQL/5.6.6和InnoDB,nginx/1.99,php-cgi/7.0.0启用zend。该脚本从网页调用,并关闭了缓冲以立即反馈。

编辑:

如果我将 4 个

32 位整数更改为 1 个二进制(16),这可能会更好地工作,这会将比较从 4 更改为 1,但我还必须将我的 4 个参数转换为 128 位字符,而 php 不会这样做。如果有一种方法可以快速组合它们,它可能会挤出更多的时间。

编辑接受的答案将速度提高了~500%。我们假设的快速概要:pHash "A" 的位计数将始终在 pHash "B" +/- 汉明距离内。

特别感谢@duskwuff的坚韧和耐心。干杯@duskwuff!

编辑这是我最近的查询:

Select
  files.`Key`, 
  Bit_Count(? ^ pHash0) + Bit_Count(? ^ pHash1) +
  Bit_Count(? ^ pHash2) + Bit_Count(? ^ pHash3) as BC
  From
    files FORCE INDEX (bitcount)
  Where
    bitCount Between ? And ? 
  AND Bit_Count(? ^ pHash0) + Bit_Count(? ^ pHash1) +
  Bit_Count(? ^ pHash2) + Bit_Count(? ^ pHash3) <= ?
  ORDER BY Bit_Count(? ^ pHash0) + Bit_Count(? ^ pHash1) +
  Bit_Count(? ^ pHash2) + Bit_Count(? ^ pHash3)
其中,前 4 个"

?"表示正在检查的文件的 4 个 32 位哈希,接下来的 2 个"?" 表示该文件的预先计算的位计数 +/- 所需的汉明距离,最后一个"?" 表示该汉明距离。ORDER BY 子句仅在将最接近的匹配项置于顶部时才是必需的,其中 LIMIT 1 子句将返回最佳匹配项。bitcount字段上有一个 B-TREE 索引。

来自 240 万个文件的位计数的分散性陷入钟形曲线,极端有 3 或 4 个,中间有 70,000 个。如果给定一个位计数为 64 的文件(这是最坏的情况),查找汉明距离为 3 的文件意味着比较 20% 的文件(在我的例子中为 490,000),而查找汉明距离为 0 将仅比较 2.8% 的记录(当然是 70,000)。

观察BIT_COUNT(a ^ b)BIT_COUNT(a)BIT_COUNT(b) 之间的差异为。(也就是说,它始终至少等于差值,并且可能更大。如果预先计算每行的总位数,则可以使用该计数来排除总位数与目标相距太远的行。更好的是,您可以在该列上创建一个索引,该索引将被使用。

我想到的是大致如下:

ALTER TABLE files ADD COLUMN totalbits INTEGER;
CREATE INDEX totalbits_index ON files (totalbits);
UPDATE files SET totalbits = BIT_COUNT(pHash1) + BIT_COUNT(pHash2)
                           + BIT_COUNT(pHash3) + BIT_COUNT(pHash4);
SELECT `Key` FROM files WHERE (totalbits BETWEEN … AND …) AND …

请注意,有了这个,就没有必要将哈希拆分为四个块。将它们合并回单个列将使事情变得更容易。