SQL Server 2005中的CHECKSUM()冲突

时间:2009-06-22 19:42:26

标签: sql sql-server-2005 checksum hash-collision

我有一个包含5,651,744行的表,主键由6列(int x 3,smallint,varchar(39),varchar(2))组成。我希望使用此表和另一个共享此主键的表以及添加的另一列但具有37m行来提高性能。

在预期添加列以创建哈希键时,我进行了分析并发现了18,733次冲突。

SELECT  SUM(CT)
FROM    (
         SELECT HASH_KEY
               ,COUNT(*) AS CT
         FROM   (
                 SELECT CHECKSUM(DATA_DT_ID, BANK_NUM, COST_CTR_NUM,
                                 GL_ACCT_NUM, ACCT_NUM, APPN_CD) AS HASH_KEY
                 FROM   CUST_ACCT_PRFTBLT
                ) AS X
         GROUP BY HASH_KEY
         HAVING COUNT(*) > 1
        ) AS Y

SELECT  COUNT(*)
FROM    CUST_ACCT_PRFTBLT

BINARY_CHECKSUM()

差不多两倍

考虑到我所覆盖的目标空间的相对较小量,这看起来是否太高(.33%)?如果碰撞很高,那么在连接中加入这个制造的密钥是否有利于每行额外4个字节的成本,因为你仍然必须加入常规列来处理偶尔的碰撞? / p>

5 个答案:

答案 0 :(得分:7)

我没有看到添加校验和的位置会让你得到任何级别的collisons。即使1次碰撞也太多,因为它会导致您加入错误的数据。如果你不能保证加入正确的记录,那么如果它提高性能但是数据完整性混乱则毫无意义。这似乎是财务数据,因此您最好确保您的查询不会返回错误结果。如果发生任何冲突,您实际上最终可能会记入借方或记入错误的帐户。

如果你选择这条路线,Marc是正确的,如果可能的话,你应该预先计算(在数百万记录表中添加必须发生的计算不太可能提高我的经验)。可能如果您可以执行预先计算的列(并且您需要触发器以使其保持更新),那么您可能不需要连接到所有其他六列以确保没有冲突。那么可能你可能会有更好的表现。你所能做的只是测试你的理论。但请确保您没有任何碰撞。

您是否考虑使用代理键,然后在六个自然键字段上使用唯一索引?然后你可以加入代理键,这可能会提高性能。连接六列(一个varchar)而不是一个代理键是无效的。我从数据的大小中意识到,这可能比非生产系统更难重构,但实际上值得花时间来永久修复持久性性能问题。只有你可以说这是一个多么复杂的变化,以及将所有sps或查询更改为更好的连接有多难。但是,尝试可能是可行的。

答案 1 :(得分:6)

到目前为止,很多人都看到了CHECKSUMMicrosoft's own admission而发生大量碰撞。它甚至比MD5更糟糕,SHA1有明显的有意义碰撞。

如果您希望获得哈希列,请考虑使用HASHBYTES并指定SHA1。  与MD5CHECKSUM相比,CHECKSUM碰撞的意义要小得多。因此,永远不应该使用HASHBYTES来确定行是否唯一,而是快速检查两个值的保真度。因此,对于HASHBYTES,您的碰撞率应为0%,除非您有重复的行(作为PK,绝不应该发生)。

请记住,{{1}}将截断大于8000字节的任何内容,但是你的PK比那些(所有连接的)少得多,所以你不应该有任何麻烦。

答案 2 :(得分:2)

如果你的校验和得到它的数据的0.33%,那么我认为它工作正常...特别是如果你将这个列与其他(索引)列结合使用。

当然,为了有效地作为索引,您可能希望在插入/更新数据时使用非聚集索引来计算和存储此值。

当然,对相关列的常规生成索引也可以做得更好或更好......

答案 3 :(得分:1)

如果您的查询是选择性的并且行表聚集索引很窄或不存在,那么行表中校验和上的非聚集索引应该提供良好的性能。

在将任何标准应用于头表后,它将使用校验和对非聚集索引执行索引查找。您仍然需要在连接中包含FK,但非索引连接条件将应用于索引后搜索,书签后查找。很有效率。

您希望针对索引搜索进行优化。校验和已经具有高度选择性。添加FK会增加索引大小和相应的I / O,除非包含足够的其他字段以完全避免书签查找,否则无济于事。

由于非聚集索引将包含聚类键或堆指针,因此您需要a)小型聚类键(例如,int标识列 - 4字节指针)或b)根本没有聚簇索引(8)字节指针)。

如果您的查询不是选择性的,或者行表聚集索引很大(整个表减去几列),那么我不知道校验和是否有帮助(更快的索引导航,也许?)。在任何情况下,您都希望将其设置为聚簇索引或覆盖索引,如果头表不首先聚集在校验和上,则会有很多排序。

如果您能负担得起存储和索引成本,可以采用一些覆盖索引 - 标题和详细信息。

答案 4 :(得分:1)

如果您的PRIMARY KEY已群集,则您创建的每个索引都会包含此PRIMARY KEY

加入散列值将使用以下步骤:

  1. 在索引键中找到散列值
    • 在索引数据中找到PRIMARY KEY
    • 使用Clustered Index Seek找到表格中的PRIMARY KEY
  2. 加入PRIMARY KEY只会使用步骤3

    然而,

    SQL Server非常聪明,可以考虑到这一点,如果你愿意这样加入:

    SELECT  *
    FROM    main_table mt
    JOIN    CUST_ACCT_PRFTBLT cap
    ON      cap.HASH_KEY = mt.HASH_KEY
            AND cap.DATA_DT_ID = mt.DATA_DT_ID
            AND …
    WHERE   mt.some_col = @filter_value
    

    ,它只是不会使用HASH_KEY上的索引,相反,它将使用单个Clustered Index SeekFilter来确保哈希值匹配(并且它们总是会匹配)

    摘要:只需加入PRIMARY KEY

    使用辅助索引,您首先需要进行无用的HASH_KEY搜索,然后仍然需要加入PRIMARY KEY