使用校验和比较许多列上的两个表?

时间:2017-01-26 15:01:31

标签: mysql database performance join database-design

我想比较具有相同列的两个巨大的表来查找具有不同值的记录。 (两个表中的许多行都是相同的)

需要花费大量时间,性能非常差,查询现在看起来像这样:

SELECT  *
    FROM  table1 a
    INNER JOIN  table2 b
       ON a.value!=b.value
      OR  a.value!=b.value
      OR  a.value!=b.value
      OR  a.value!=b.value
      OR  a.value!=b.value
      OR  a.value!=b.value
      OR  a.value!=b.value OR...
    WHERE  a.id=1
      AND  b.id=2
      AND  a.specialvalue=b.specialvalue;

想法是创建我需要比较的所有值的校验和,并仅比较行的校验和。我的问题是:这是一个好方法吗?性能会有提升吗?是否有可能在合理的时间内将两行与100k记录进行比较?

3 个答案:

答案 0 :(得分:1)

如果你需要检查每个100K的100K行,那就是大约50 亿比较。这将花费比你想象的更长的时间。但是你可以这样做:

CREATE Cksums (
    md5 CHAR(32) CHARACTER SET ascii,
    id ...,   -- PK of the row
    INDEX(md5)
);

INSERT INTO Cksums (md5, id)
    SELECT
           MD5(CONCAT_WS("\t", col2, col3, ...),
           id
        FROM your_table;

然后查找dup行:

SELECT md5,
       GROUP_CONCAT(id) AS dup_ids
    FROM Cksums
    GROUP BY md5
    HAVING COUNT(*) > 1;

DROP TABLE Cksums;  -- cleanup

这将通过两次100K表扫描有效地进行500亿次比较(一次用于构建Cksums,一次用于检查)。

注意事项:

  • 我不知道这是否能很好地处理NULLs
  • 我认为" tab"未在任何列中使用。
  • BLOBs应通过HEX(the_blob)提取,以避免CONCAT_WS中的各种问题。
  • 如果太多行相同(数百),GROUP_CONCAT将截断列表。
  • 我声称(有些人不同意)128位MD5就足够了。如果你有9万亿md5s,那么在9万亿只中只有一次会有两次错误相同。

答案 1 :(得分:1)

检查两个表是否相同的快速方法是使用MySQL的CHECKSUM TABLE命令。如果两个表给出相同的校验和,那么两个表中的所有数据完全同步,你就完成了。

如果校验和不同,那么您可以查看更多细节以找出哪些行不同。

Percona Toolkit包含一个工具pt-table-sync,可以更有效地执行两个表之间的数据比较。你需要安装Perl,以及Perl模块DBI和DBD :: mysql。

例如,我想在我的测试数据库中比较两个相同的表,这些表称为死锁和死锁2。首先,我只是想看看有什么不同之处:

$ pt-table-sync --print h=localhost,D=test,t=deadlocks t=deadlocks2

输出显示了一系列INSERT / UPDATE / DELETE语句,这些语句是使deadlocks2表与deadlocks表具有相同数据所必需的。

如果您已准备好同步它们,请将print更改为执行,它将执行SQL更改:

$ pt-table-sync --execute h=localhost,D=test,t=deadlocks t=deadlocks2

之后,每个表的CHECKSUM TABLE应匹配。

另一个选项是来自MySQL Utilities的mysqldbcompare。我没有这个工具的经验,但似乎它将比较两个完整数据库的所有表中的数据。我不知道是否可以选择比较具有不同名称但结构相似的两个表。

Percona Toolkit和MySQL Utilities都是免费软件。

答案 2 :(得分:1)

这里有一些很棒的答案,包括我最喜欢的pt-table-checksum

如果您只需要执行此操作并希望快速找到差异,请考虑使用mysqldumpSELECT ... INTO OUTFILE将表转储为两个单独的文件,然后使用旧的{{在命令行上执行命令。

相关问题