10

我有一个包含 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()

考虑到我覆盖的目标空间相对较小,这是否看起来太高(0.33%)?如果冲突如此之高,那么在连接中首先加入这个制造的密钥是否有好处,因为您仍然必须加入常规列以处理偶尔的冲突?

4

5 回答 5

8

我看不出在哪里添加校验和会给你带来那种级别的碰撞。即使 1 次冲突也太多了,因为它会导致您加入错误的数据。如果您不能保证加入正确的记录,那么如果它提高了性能但会破坏数据完整性,那将毫无意义。这似乎是财务数据,因此您最好确保您的查询不会返回错误的结果。如果发生任何冲突,您实际上最终可能会借记或贷记错误的帐户。

如果您确实走这条路,那么 Marc 是正确的,您应该尽可能进行预计算(根据我的经验,添加必须对数百万记录表中的每条记录进行的计算不太可能提高性能)。可能如果您可以执行预先计算的列(并且您需要触发器以使其保持最新),那么您可能不需要加入所有其他六个列以确保没有冲突。那么您可能会提高性能。你所能做的就是检验你的理论。但要非常确定你没有任何碰撞。

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

于 2009-06-22T20:52:24.493 回答
7

到目前为止,我看到很多人都在掩饰的是,微软自己承认CHECKSUM,这有很多冲突。它甚至比 , 更糟糕的是,它有相当多的有意义的碰撞。MD5

如果您要获取哈希列,请考虑使用HASHBYTESwith SHA1specified。 与or相比,SHA1有意义的碰撞要少得多。因此,永远不应使用它来确定一行是否唯一,而是快速检查两个值的保真度。因此,你的冲突率应该是 0% ,除非你有重复的行(作为一个 PK,不应该发生)。MD5CHECKSUMCHECKSUMHASHBYTES

请记住,这HASHBYTES将截断大于 8000 字节的任何内容,但您的 PK 远小于该值(全部连接),因此您不应该遇到任何麻烦。

于 2009-07-06T10:43:31.407 回答
2

如果您的校验和将其降至数据的 0.33%,那么我认为它工作正常……特别是如果您将此列与其他(索引)列结合使用。

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

当然,有问题的列上的常规跨越索引可能会做得一样好或更好......

于 2009-06-22T19:45:43.367 回答
1

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

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

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

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

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

如果你能负担得起存储和索引成本,一些覆盖索引——标题和细节——可能是要走的路。

于 2009-06-24T03:24:26.763 回答
1

如果您PRIMARY KEY是集群的,那么您创建的每个索引都将包含 this PRIMARY KEY

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

  1. 在索引键中找到散列值
    • PRIMARY KEY在索引数据中定位值
    • 用于Clustered Index Seek定位PRIMARY KEY表中的行

加入 aPRIMARY KEY将仅使用 step 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 Seek和 aFilter来确保哈希值匹配(并且它们总是会匹配)。

总结:只要加入就行了PRIMARY KEY

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

于 2009-06-24T16:00:48.420 回答