6

我们有一个遗留数据库,它是一个 sql server db(2005 和 2008)。

表中的所有主键都是唯一标识符。

这些表目前没有在它们上创建聚集索引,并且我们在只有 750k 记录的表上遇到了性能问题。这是我使用的第一个使用唯一标识符作为唯一主键的数据库,我从未见过 sql server 在返回数据时如此缓慢。

我不想在 uniqueidentifier 上创建聚集索引,因为它们不是顺序的,因此在插入数据时会减慢应用程序的速度。

我们无法删除用于远程站点记录身份管理目的的唯一标识符。

我曾考虑向表中添加一个大整数标识列,并在该列上创建聚集索引并包括唯一标识符列。

IE

int identity - 保持插入速度的第一列唯一标识符 - 确保应用程序按预期工作。

目标是提高标识查询和联接表查询性能。

Q1:这会提高数据库的查询性能还是会减慢它的速度?

Q2:有没有我没有列出的替代品?

谢谢皮特

编辑: 性能问题是通过选择语句快速检索数据,特别是如果一些更多的“事务/更改”表连接在一起。

编辑2:表之间的连接通常都在主键和外键之间,对于具有外键的表,它们包含在非聚集索引中以提供更多覆盖索引。

这些表都没有其他可以提供良好聚集索引的值。

我更倾向于在每个高负载表上添加一个额外的标识列,然后在聚集索引中包含当前的 Guid PK 列以提供最佳查询性能。

编辑 3:我估计 80% 的查询是通过数据访问机制单独对主键和外键执行的。通常,我们的数据模型具有延迟加载的对象,这些对象在访问时执行查询,这些查询使用对象 id 和 PK 列。我们有大量用户驱动的数据排除/包含查询,它们使用外键列作为过滤器,基于类型 X 的标准排除以下 id。剩下的 20% 是枚举 (int) 或日期范围列的 where 子句,系统中执行的基于文本的查询很少。

在可能的情况下,我已经添加了覆盖索引来覆盖最重的查询,但到目前为止我仍然对性能感到失望。正如 bluefooted 所说,数据被存储为堆。

4

4 回答 4

4

如果表上没有聚集索引,则它被存储为堆而不是 b 树。堆数据访问在 SQL Server 中绝对是残酷的,所以你肯定需要添加一个聚集索引。

我同意您的分析,即 GUID 列对于聚类来说是一个糟糕的选择,特别是因为您没有使用 NEWSEQUENTIALID() 的能力。如果愿意,您可以创建一个新的人工整数键,但如果有另一列或列组合作为聚集索引有意义,那也可以。

您是否有经常用于范围扫描的字段?哪些列用于连接?除了 GUID 之外,是否存在也唯一标识行的列组合?发布数据模型的样本将帮助我们建议一个好的聚类候选者。

于 2010-08-21T02:13:59.733 回答
2

我不确定您的 GUID 来自哪里,但如果它们是在插入期间生成的,那么在 SQL Server 中使用NEWSEQUENTIALID()而不是NEWID()将帮助您避免在插入期间出现碎片问题。

关于聚簇索引的选择,正如 Kimberly L. Tripp 所说:“选择聚簇索引最重要因素是它是唯一的、狭窄的和静态的(不断增加对最小化拆分还有其他好处)。” 与 INT 甚至 BIGINT 相比,GUID 无法满足狭义的要求。

Kimberly 也有一篇关于GUID 作为 PRIMARY KEYs 和/或 clustering key的优秀文章。

于 2010-08-20T21:31:04.870 回答
1

我不是 100% 清楚:您的 1 号访问模式是通过 GUID 还是其他列查询表?在加入其他表时,最常使用哪些列(和数据类型)?

在我了解更多关于如何使用这些 GUID 之前,我真的无法给你任何可靠的建议。我意识到你说它们是主键,但这并不能保证它们被用作查询或连接的主要条件。

更新

现在我知道的更多了,我有一个疯狂的建议。将这些表聚集在 GUID 上,但将填充因子设置为 60%。这将改善页面拆分问题,并为您提供更好的性能查询这些小狗。

至于使用 Guid.NewGuid(),看来您毕竟可以在 C# 中执行顺序 GUID。我在 SO 上找到了以下代码:

[DllImport("rpcrt4.dll", SetLastError = true)]
static extern int UuidCreateSequential(out Guid guid);

public static Guid SequentialGuid()
{
    const int RPC_S_OK = 0;
    Guid g;
    if (UuidCreateSequential(out g) != RPC_S_OK)
        return Guid.NewGuid();
    else
        return g;
}

newsequentialID() 实际上只是 UuidCreateSequential 的一个包装器。我敢肯定,如果您不能直接在客户端上使用它,您可以找到一种方法来快速往返服务器以从那里获取新的顺序 ID,甚至可以使用“分配器”表和存储过程来完成这项工作。

于 2010-08-21T02:57:07.387 回答
0

您没有说明您的性能问题是什么。如果执行最差的操作是 INSERT,那么您的解决方案可能是正确的。如果它是别的东西,那么我会看看聚集索引如何帮助它。

您可能会查看表上的现有索引以及使用它们的查询。您可以选择一个索引,虽然会稍微降低 INSERT,但可以为当前的性能问题区域提供更大的好处。

于 2010-08-20T21:44:31.870 回答