汉明距离优化MySQL或PostgreSQL?

2guxujil  于 2023-02-15  发布在  Mysql
关注(0)|答案(3)|浏览(161)

我试图改善搜索相似的图片pHash在MySQL数据库.现在我比较pHash计数汉明距离这样:

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

选择 (引擎MyISAM) 的结果

  • 20000行;查询时间〈20ms
  • 10万行;查询时间~60ms #这刚刚好,直到达到150000行
  • 30万行;查询时间~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

30万行;查询时间~240ms
我改变了数据库引擎PostgreSQL。Translate this MySQL query to PyGreSQL没有成功。行300000;查询时间~18秒

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

我有有限的方法(工具)来解决这个问题。MySQL到目前为止似乎是最简单的解决方案,但我可以部署代码的每一个开源数据库引擎,将与Ruby的专用机器。有一些现成的解决方案MsSQL https://stackoverflow.com/a/5930944/766217(未测试)。也许有人知道如何翻译它的MySQL或PostgreSQL。
请根据一些代码或观察结果给出答案。我们在www.example.com上有很多关于汉明距离的理论问题stackoverflow.com
谢谢!

xu3bshqb

xu3bshqb1#

在考虑算法的效率时,计算机科学家使用了 order 的概念,表示为O(something),其中something是n的函数,,是要计算的东西的数量,在本例中是行数,因此,随着时间的增加,我们得到:

  • O(1)-与项数无关
  • O(log(n))-随项目的对数增加
  • O(n)-增加的项目比例(你有什么)
  • O(n^2)-随着项的平方而增加
  • O(n^3)-等
  • 复杂度O(2)
  • O(n!)-随该数的阶乘而增加

对于任何合理的n(80+)数,最后2个实际上是不可计算的。
只有最重要的一项是重要的,因为这对大的n占优势,所以n^2和65n^2+787n+4656566都是O(n^2)
请记住,这是一个数学构造,并且使用真实数据的真实硬件上的真实的软件的算法所花费的时间可能受到其他事情的严重影响(例如,O(n^2)存储器操作可能比O(n)磁盘操作花费更少的时间)。
对于您的问题,您需要遍历每一行并计算BIT_COUNT(hash ^ 2028359052535108275) <= 4,这是一个O(n)操作。
由于b树索引检索是一个O(log(n))操作,因此唯一可以改进的方法是利用索引。
但是,由于列字段包含在函数中,因此不能使用该列的索引。有两种可能性:
1.这是一个SQL Server解决方案,我不知道它是否可以移植到MySQL。在表中创建一个持久化计算列,公式为BIT_COUNT(hash ^ 2028359052535108275),并在其上放置索引。如果您需要更改位掩码,这将 * 不 * 适合。
1.找出一种不使用BIT_COUNT函数进行位运算的方法。

k5ifujac

k5ifujac2#

这个解决方案让我的速度快了一些,它为每个哈希比较生成一个派生表,并且只返回小于ham距离的结果,这样就不用对已经超过ham距离的pHash执行BIT_COUNT,它在大约2.25秒内返回260万条记录的所有匹配。
它是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

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

vof42yt1

vof42yt13#

下面是我的测试结果:Phash是用Python中的imagehash库计算出来的,并作为两个BIGINT存储在数据库中。
这个测试是在mariadb数据库中的858,433张图片上运行的,我发现分片实际上会减慢这个过程,但是这是使用函数方法的,所以没有它或者在一个大的数据库上可能会有所不同。
运行这些命令的表是一个只在内存中的表,它保留了一个本地表,在数据库启动时,id、phash1和phash2被复制到内存中的表中,一旦发现了什么,就会返回id以匹配innodb表。

    • 图像总数:小行星858433**
    • 图片1:ece0455d6b8e9470**

功能汉明距离_16:

RETURN BIT_COUNT(A0 ^ B0) + BIT_COUNT(A1 ^ B1)

方法:汉明距离_16函数
质询:

SELECT `id` FROM `phashs` WHERE HAMMINGDISTANCE_16(filephash_1, filephash_2, CONV(SUBSTRING('ece0455d6b8e9470', 1,  8), 16, 10), CONV(SUBSTRING('ece0455d6b8e9470', 9,  8), 16, 10)) <= 3;

时间:2.1760秒
方法:位计数
质询:

SELECT `id` FROM `phashs` WHERE BIT_COUNT(filephash_1 ^ CONV(SUBSTRING('ece0455d6b8e9470', 1,  8), 16, 10)) +  BIT_COUNT(filephash_2 ^ CONV(SUBSTRING('ece0455d6b8e9470', 9,  8), 16, 10)) <= 3;

时间:0.1547秒
方法:多选BIT_COUNT内部为文件hash_1
质询:

SELECT `id` FROM ( SELECT `id`, `filephash_2`, BIT_COUNT(filephash_1 ^ CONV(SUBSTRING('ece0455d6b8e9470', 1, 8), 16, 10)) as BC0 FROM `phashs` WHERE BIT_COUNT(filephash_1 ^ CONV(SUBSTRING('ece0455d6b8e9470', 1, 8), 16, 10)) <= 3 ) BCQ0 WHERE BC0 + BIT_COUNT(filephash_2 ^ CONV(SUBSTRING('ece0455d6b8e9470', 9, 8), 16, 10)) <= 3;

时间:0.1878秒
方法:多选BIT_COUNT内部为文件hash_2
质询:

SELECT `id` FROM (SELECT `id`, `filephash_1`, BIT_COUNT(filephash_2 ^ CONV(SUBSTRING('ece0455d6b8e9470', 9,  8), 16, 10)) as BC1 FROM `phashs` WHERE  BIT_COUNT(filephash_2 ^ CONV(SUBSTRING('ece0455d6b8e9470', 9,  8), 16, 10)) <= 3) BCQ1 WHERE BIT_COUNT(filephash_1 ^ CONV(SUBSTRING('ece0455d6b8e9470', 1,  8), 16, 10)) + BC1   <= 3;

时间:0.1860秒

    • 图片2:813ed36913ec8639**

功能汉明距离_16:

RETURN BIT_COUNT(A0 ^ B0) + BIT_COUNT(A1 ^ B1)

方法:汉明距离_16函数查询:

SELECT `id` FROM `phashs` WHERE HAMMINGDISTANCE_16(filephash_1, filephash_2, CONV(SUBSTRING('813ed36913ec8639', 1,  8), 16, 10), CONV(SUBSTRING('813ed36913ec8639', 9,  8), 16, 10)) <= 3;

时间:2.1440秒
方法:BIT_COUNT查询:

SELECT `id` FROM `phashs` WHERE BIT_COUNT(filephash_1 ^ CONV(SUBSTRING('813ed36913ec8639', 1,  8), 16, 10)) +  BIT_COUNT(filephash_2 ^ CONV(SUBSTRING('813ed36913ec8639', 9,  8), 16, 10)) <= 3;

时间:0.1588秒
方法:多选BIT_COUNT内部为文件hash_1
质询:

SELECT `id` FROM ( SELECT `id`, `filephash_2`, BIT_COUNT(filephash_1 ^ CONV(SUBSTRING('813ed36913ec8639', 1, 8), 16, 10)) as BC0 FROM `phashs` WHERE BIT_COUNT(filephash_1 ^ CONV(SUBSTRING('813ed36913ec8639', 1, 8), 16, 10)) <= 3 ) BCQ0 WHERE BC0 + BIT_COUNT(filephash_2 ^ CONV(SUBSTRING('813ed36913ec8639', 9, 8), 16, 10)) <= 3;

时间:0.1671秒
方法:多选BIT_COUNT内部为文件hash_2
质询:

SELECT `id` FROM (SELECT `id`, `filephash_1`, BIT_COUNT(filephash_2 ^ CONV(SUBSTRING('813ed36913ec8639', 9,  8), 16, 10)) as BC1 FROM `phashs` WHERE  BIT_COUNT(filephash_2 ^ CONV(SUBSTRING('813ed36913ec8639', 9,  8), 16, 10)) <= 3) BCQ1 WHERE BIT_COUNT(filephash_1 ^ CONV(SUBSTRING('813ed36913ec8639', 1,  8), 16, 10)) + BC1   <= 3;

时间:0.1686秒

相关问题