聚集索引帮助,我哪里出错了?

tre*_*nja 7 sql-server clustered-index sql-server-2008-r2 index-tuning

我被要求查看一张非常繁忙的表格并找出任何需要改进的地方。
我只能更改索引表的能力非常有限。

表信息

  • 240 列
  • 约 500 万行
  • 由大约 30 个应用程序阅读和更新,范围从网站到投票应用程序。
  • 每行代表一个合约及其基于三个标志(大小为 5、8 和 8 的 varchars)的状态。
  • 一行的生命周期通过这三个标志从开始到结束,最终完成。
  • 在整个生命周期中,一行通常会更新或更改 10 到 30 次。
  • 主键是 id 列、guid 列和 company 列的组合。
  • 表有 40 多个索引,其中大部分是重复的和未使用的。这是基于sys.dm_db_index_usage_statsDMV,在过去 7 周内每周运行两次。

此表上的当前聚集索引有五列:

  1. 公司列(50 个不同的值)
  2. 区域列(21 个不同的值)
  3. FlagA 列(8 个不同的值)
  4. FlagB 列(24 个不同的值)
  5. FlagC 列(5 个不同的值)

我的理解是聚簇索引应该坚持以下属性。 来源

  1. 独特的
  2. 静止的
  3. 狭窄的
  4. 不断增加。

当前的聚集索引不是这些。

  1. 没有唯一的 ID。
  2. 这三个标志不断更新。
  3. 任何给定时刻都可以有 5000 行所有 5 列都具有相同的值。

所以我的假设是用Id列上的聚集索引来纠正这个问题- 一个不是身份但通过计数器表维护的整数(读取值,加 1,更新计数器表)。

我在 上创建了一个聚集索引Id,不使用主键,因为我相信添加 guid 和 company 列不会给我任何好处。

然后我创建了一个非聚集索引,其中包含 Company、Region 和 3 标志。在测试环境中,统计数据看起来不错,user_updates但较低等。但是针对该表的应用程序的整体性能很糟糕。针对该表的最常见查询是:

SELECT * 
FROM table 
WHERE ID = 1234;
Run Code Online (Sandbox Code Playgroud)

SELECT * 
FROM table 
WHERE Company = 'company' 
AND Region = 'region' 
AND flagA= 'A'
AND flagB = 'B'
AND flagC = 'C';
Run Code Online (Sandbox Code Playgroud)
  • 我错过了什么?上述聚集索引规则是否有例外?
  • 将公司和地区添加到 id 会不会使聚集索引受益?

我的理解是,如果聚集索引位于非静态值上,它需要不断地重新组织自身和其他非聚集索引,此时我们还有 40 多个……稍后将被删除。我不会在那里看到收益吗?

添加了信息。

  • 两种环境都是 SQL Server 2008 R2
  • 具有相同的操作系统、内存等。
  • 唯一的区别是聚集索引。
  • SELECT *在上面的例子中写了简洁。
  • 测试和生产环境尽可能地“相同”:生产面临更重的负载,但两者都运行相同的应用程序,负载平衡等。测试明显变慢。
  • 在回滚到原始设置之前,我们使用新集群运行了一个半星期。最初,随着缓慢的性能开始出现,更新统计信息并重新编译特定的存储过程。
  • 我同意多列索引至少表的组织比仅在Id. 我想重新尝试使用 Company、Region 和 Id 作为 CI,但将三个标志排除在外。

我错过了什么?

Pau*_*ite 3

上述聚集索引规则是否有例外?

选择聚集索引的一般指南很好,但有时需要考虑其他注意事项。有时,这些额外因素可能比一般“规则”更重要。

您的场景有些“特殊”,因为您有一个非常宽的表,并且有一组查询请求一组(大概)不可预测的列,尽管查询谓词通常是相同的。

最初的聚集索引安排对于更改数据的操作来说可能代价高昂,因为更改聚集键的任何部分都意味着更改所有非聚集索引。此外,在日志生成方面,物理移动整行的成本很高,尤其是在发生页面拆分的情况下。

也就是说,一旦聚集索引被修改并分割了相当多的部分,它将包含相当多的可用空间,使未来的移动不那么密集,就像一开始就设置了合理的填充因子一样,并保持为正常索引维护的一部分。

您的生产系统似乎已经陷入某种平衡,其中页面分割以稳定、合理的速率发生。非聚集索引维护仍然相对昂贵,但这似乎不是一个主导因素。

原始索引安排的关键优点是:

  1. 聚集索引(Company、Region、FlagA、FlagB、FlagC)与以下谓词匹配:

    SELECT {unpredictable column list}
    FROM table 
    WHERE Company = 'company' 
    AND Region = 'region' 
    AND flagA= 'A'
    AND flagB = 'B'
    AND flagC = 'C';
    
    Run Code Online (Sandbox Code Playgroud)

    ...同时提供对选择列表中列出的任何列的访问。

  2. 查询形式:

    SELECT * 
    FROM table 
    WHERE ID = 1234;
    
    Run Code Online (Sandbox Code Playgroud)

    ...非聚集主键充分支持。此查询始终返回单行,因此在非聚集索引中定位该行后,只需要通过聚集索引进行一次查找。

更改为 上的聚集索引ID以及 (Company、Region、FlagA、FlagB、FlagC) 上的非聚集索引使第二个查询的效率更高一些(消除每个查询的一次查找),但使第一个查询的效率大大降低(替换零)查找~5000)。

此外,优化器很可能选择根本不使用非聚集索引,估计对表进行完整扫描会比 5000 次查找更便宜。

您最好暂时保留两个主要索引,同时整理 40 多个非聚集索引,并分析所需的最小覆盖非聚集索引集的工作负载。一旦获得该数据,您将能够更好地考虑基本指数变化。

您可能还想检查此表的现有监控和维护例程。在许多情况下,如果表的填充因子能够在下一个维护窗口到来之前防止出现严重的页面拆分,那么“打破规则”的聚集索引就可以了。