【问题标题】:Hamming distance on binary strings in SQLSQL中二进制字符串的汉明距离
【发布时间】:2011-06-14 04:57:45
【问题描述】:

我的数据库中有一个表,我将 SHA256 哈希值存储在 BINARY(32) 列中。我正在寻找一种方法来计算列中的条目与提供的值的汉明距离,例如:

SELECT * FROM table 
  ORDER BY HAMMINGDISTANCE(hash, UNHEX(<insert supplied sha256 hash here>)) ASC 
  LIMIT 10

(如果您想知道,字符串 A 和 B 的汉明距离定义为 BIT_COUNT(A^B),其中 ^ 是按位异或运算符,BIT_COUNT 返回二进制字符串中 1 的数量)。

现在,我知道 ^ 运算符和 BIT_COUNT 函数都只适用于整数,所以我想说可能唯一的方法是将二进制字符串分解为子字符串,将每个二进制子字符串转换为整数,计算汉明距离子串,然后将它们相加。问题在于它听起来非常复杂,效率不高,而且绝对不优雅。因此,我的问题是:您能提出更好的方法吗? (请注意,我使用的是共享主机,因此无法修改数据库服务器或加载库)

edit(1):显然在 PHP 中加载整个表并在那里进行计算是可能的,但我宁愿避免它,因为这个表可能会变得非常大。

edit(2):数据库服务器是 MySQL 5.1

edit(3):我下面的答案包含我刚才描述的代码。

edit(4):我刚刚发现使用 4 个 BIGINT 而非 BINARY(32) 来存储散列可以大大提高速度(快 100 倍以上)。请参阅下面我的答案的 cmets。

【问题讨论】:

  • 如果这对找到更好的解决方案有用,也可以提出不同的方法来存储哈希值。
  • 如果您将哈希存储在 8 个整数中(可能除了二进制存储之外),计算会变得容易得多。
  • 我真的很好奇你为什么要计算距离:)
  • 我正在处理一个分布式哈希表,并且需要该步骤来在其他节点上查找密钥(即,如果您在本地没有密钥,则将查询转发到 ID 为更接近键 - 更接近是根据汉明距离测量的);显然,ID 和密钥的长度相同(在我的情况下为 256 位)。
  • “表格可能会变得很大”是什么意思?是否可以接受具有足够功能的全扫描,或者您是否需要一种使用某些索引并避免全扫描的方法?

标签: sql mysql hash binary-data hamming-distance


【解决方案1】:

有趣的问题,我找到了一种方法可以为 binary(3) 执行此操作,并且可能也适用于 binary(32)

drop table if exists BinaryTest;
create table  BinaryTest (hash binary(3));
insert BinaryTest values (0xAAAAAA);

set @supplied = cast(0x888888 as binary);

select  length(replace(concat(
            bin(ascii(substr(hash,1,1)) ^ ascii(substr(@supplied,1,1))),
            bin(ascii(substr(hash,2,1)) ^ ascii(substr(@supplied,2,1))),
            bin(ascii(substr(hash,3,1)) ^ ascii(substr(@supplied,3,1)))
        ),'0',''))
from    BinaryTest;

replace 删除任何全零,余数的长度是个数。 (转换为二进制会省略前导零,因此无法计算零。)

这会打印出6,它与

中的个数相匹配
0xAAAAAA ^ 0x888888 = 0x222222 = 0b1000100010001000100010

【讨论】:

    【解决方案2】:

    似乎将数据存储在BINARY 列中是一种必然会表现不佳的方法。获得良好性能的唯一快速方法是将 BINARY 列的内容拆分为多个 BIGINT 列,每个列包含原始数据的 8 字节子字符串。

    在我的情况下(32 字节),这意味着使用 4 个BIGINT 列并使用此函数:

    CREATE FUNCTION HAMMINGDISTANCE(
      A0 BIGINT, A1 BIGINT, A2 BIGINT, A3 BIGINT, 
      B0 BIGINT, B1 BIGINT, B2 BIGINT, B3 BIGINT
    )
    RETURNS INT DETERMINISTIC
    RETURN 
      BIT_COUNT(A0 ^ B0) +
      BIT_COUNT(A1 ^ B1) +
      BIT_COUNT(A2 ^ B2) +
      BIT_COUNT(A3 ^ B3);
    

    在我的测试中,使用这种方法比使用 BINARY 方法快 100 多倍。


    FWIW,这是我在解释问题时暗示的代码。欢迎使用更好的方法来完成同样的事情(我特别不喜欢二进制>十六进制>十进制转换):

    CREATE FUNCTION HAMMINGDISTANCE(A BINARY(32), B BINARY(32))
    RETURNS INT DETERMINISTIC
    RETURN 
      BIT_COUNT(
        CONV(HEX(SUBSTRING(A, 1,  8)), 16, 10) ^ 
        CONV(HEX(SUBSTRING(B, 1,  8)), 16, 10)
      ) +
      BIT_COUNT(
        CONV(HEX(SUBSTRING(A, 9,  8)), 16, 10) ^ 
        CONV(HEX(SUBSTRING(B, 9,  8)), 16, 10)
      ) +
      BIT_COUNT(
        CONV(HEX(SUBSTRING(A, 17, 8)), 16, 10) ^ 
        CONV(HEX(SUBSTRING(B, 17, 8)), 16, 10)
      ) +
      BIT_COUNT(
        CONV(HEX(SUBSTRING(A, 25, 8)), 16, 10) ^ 
        CONV(HEX(SUBSTRING(B, 25, 8)), 16, 10)
      );
    

    【讨论】:

    • 我刚刚进行了一些测试:使用此处定义的函数在具有 100000 行的表上运行原始问题中的查询大约需要 2.5 秒。由于我并不真正需要查询的确切答案,因此我可以通过添加 WHERE RAND()
    • 其他测试:我创建了一个将每个 BINARY(32) 转换为四个 BIGINT 的视图。这将时间从 2.5 秒缩短到 0.6 秒。
    • 好的,我发现如果我真的使用一个表来存储哈希为 4 个 BIGINT,相同的查询在 0.02 秒内完成。绝对使用 BINARY(32) 是一个坏主意(TM)。
    • 嗨,我也在尝试用 mysql 做汉明距离。究竟如何查询以哈希字符串搜索相关记录?
    • 您是否尝试过使用 BIT 列类型? dev.mysql.com/doc/refman/5.0/en/bit-type.html
    猜你喜欢
    • 2017-04-03
    • 1970-01-01
    • 2016-06-06
    • 2011-09-17
    • 2019-06-07
    • 1970-01-01
    • 2011-07-29
    • 2015-09-06
    • 2017-09-10
    相关资源
    最近更新 更多