MySQL或PostgreSQL的汉明距离优化?

时间:2013-02-17 19:30:31

标签: mysql sql query-optimization hamming-distance phash

我试图在MySQL数据库中改进搜索类似图像的pHashed。 现在我比较这样的pHash计算汉明距离:

SELECT * FROM images WHERE BIT_COUNT(hash ^ 2028359052535108275) <= 4

选择(引擎MyISAM)

的结果
  • 20000行;查询时间&lt; 20ms的
  • 100000行;查询时间~60ms#这很好,直到达到150000行
  • 300000行;查询时间~150ms

因此查询时间增加取决于表中的行数。


我也尝试在stackoverflow上找到解决方案 Hamming distance on binary strings in SQL

SELECT * FROM images WHERE 
BIT_COUNT(h1 ^ 11110011) + 
BIT_COUNT(h2 ^ 10110100) + 
BIT_COUNT(h3 ^ 11001001) + 
BIT_COUNT(h4 ^ 11010001) + 
BIT_COUNT(h5 ^ 00100011) + 
BIT_COUNT(h6 ^ 00010100) + 
BIT_COUNT(h7 ^ 00011111) + 
BIT_COUNT(h8 ^ 00001111) <= 4

行300000;查询时间~240ms


我将数据库引擎更改为PostgreSQL。 Translate this MySQL query to PyGreSQL 没有成功。 行300000;查询时间〜18s


是否有优化上述查询的解决方案? 我的意思是优化不依赖于行数。

我有限的方法(工具)来解决这个问题。 MySQL到目前为止似乎是最简单的解决方案,但我可以在每个开源数据库引擎上部署代码,该引擎将在专用机器上使用Ruby。 MsSQL https://stackoverflow.com/a/5930944/766217有一些现成的解决方案(未经测试)。也许有人知道如何为MySQL或PostgreSQL翻译它。

请根据一些代码或观察结果发布答案。我们在stackoverflow.com上有很多关于汉明距离的理论问题

谢谢!

2 个答案:

答案 0 :(得分:3)

当考虑算法的效率时,计算机科学家使用表示为O(某事物)的 order 的概念,其中某事物是n的函数,即计算的事物的数量,在这种情况下是行。所以我们越来越多地得到了:

  • O(1) - 与项目数无关
  • O(log(n)) - 以项目的对数
  • 增加
  • O(n) - 项目比例增加(你拥有的)
  • O(n ^ 2) - 以项目的平方增加
  • O(n ^ 3) - etc
  • O(2 ^ n) - 呈指数增长
  • O(n!) - 随着数字的阶乘而增加

对于任何合理数量的n(80 +),最后2个实际上是不可计算的。

只有最重要的术语才重要,因为这对大n来说占主导地位所以n ^ 2和65 * n ^ 2 + 787 * n + 4656566都是O(n ^ 2)

请记住,这是一种数学结构,算法使用实际数据在真实硬件上使用真实软件所花费的时间可能会受到其他因素的严重影响(例如,O(n ^ 2)内存操作可能比O(n)磁盘操作)。

对于您的问题,您需要遍历每一行并计算BIT_COUNT(hash ^ 2028359052535108275) <= 4。这是O(n)操作。

这可以改进的唯一方法是使用索引,因为b树索引检索是一个O(log(n))操作。

但是,由于您的列字段包含在函数中,因此无法使用该列的索引。你有两种可能性:

  1. 这是一个SQL服务器解决方案,我不知道它是否可以移植到MySQL。使用公式BIT_COUNT(hash ^ 2028359052535108275)在表中创建一个持久计算列,并在其上放置索引。如果您需要更改位掩码,这将
  2. 计算出一种不使用BIT_COUNT函数进行按位运算的方法。

答案 1 :(得分:2)

这个解决方案让我的事情变得更快。 它为每个哈希比较生成一个派生表,并仅返回小于哈希距离的结果。这样,它就没有在已经超过火腿的pHash上进行BIT_COUNT。它在260万条记录中以大约2.25秒的速度返回所有匹配项。

这是InnoDB,我的索引很少。

如果有人能加快速度,我会很感激你的。

SELECT *, BIT_COUNT(pHash3 ^ 42597524) + BC2 AS BC3 
FROM ( 
    SELECT *, BIT_COUNT(pHash2 ^ 258741369) + BC1 AS BC2 
    FROM ( 
        SELECT *, BIT_COUNT(pHash1 ^ 5678910) + BC0 AS BC1 
        FROM ( 
            SELECT `Key`, pHash0, pHash1, pHash2, pHash3, BIT_COUNT(pHash0 ^ 1234567) as BC0 
            FROM files 
            WHERE  BIT_COUNT(pHash0 ^ 1234567) <= 3 
        ) AS BCQ0 
        WHERE BIT_COUNT(pHash1 ^ 5678910) + BC0 <= 3 
    ) AS BCQ1 
    WHERE BIT_COUNT(pHash2 ^ 258741369) + BC1 <= 3 
    ) AS BCQ2 
WHERE BIT_COUNT(pHash3 ^ 42597524) + BC2 <= 3

这是等效查询,但没有派生表。它的返回时间几乎是其3倍。

SELECT `Key`, pHash0, pHash1, pHash2, pHash3 
FROM Files 
WHERE BIT_COUNT(pHash0 ^ 1234567) + BIT_COUNT(pHash1 ^ 5678910) + BIT_COUNT(pHash2 ^ 258741369) + BIT_COUNT(pHash3 ^ 42597524) <=3

请记住,第一个火腿值越低,它的运行速度就越快。