T2P*_*2PS 9 sql-server azure-sql-database sql-server-2012 sql-server-2014 sql-server-2016
我们的团队继承了一个应用程序和相关的数据库。以前的开发人员似乎强制执行了一个规则,即每个表上的每个索引都有一个 INCLUDE 子句,以始终添加不属于键的每一列。这些表平均有两到五个索引或唯一约束以及外键。
目的似乎是提高 SELECT 性能,无论向数据库抛出什么查询,因为访问是通过 ORM 进行的,默认情况下(但并非总是)检索所有列。我们预计这样做的副作用是增加了存储需求(可能显着增加)和 INSERT/UPDATE/DELETE 的额外开销时间。
问题是,这是一个明智的策略吗?我们的团队有使用 SQL Server 的历史,但没有成员认为自己是其内部行为的专家(尽管有人提出问题,如果这种策略是最佳的,现在不是默认吗?)。我们应该期待哪些其他副作用(数据库服务器 CPU/内存/TempDB 使用等),或者我们上面的一些假设是不正确的?
此外,该应用程序可以安装到本地 SQL Server(自 2012 年以来的版本)以及 Azure SQL 中——我们是否应该为两者之间的任何差异做好准备,或者因此对 Azure 产生额外的副作用方法?
我之前已经在特定索引上完成了这项工作,以帮助经常运行繁重的查询。他们所做的实际上是创建多个聚集索引:当这些索引中的任何一个用于查找行时,不需要额外的工作来查找实际聚集索引中的其余数据(如果没有真正的聚集索引,则查找堆) .
这是一个明智的策略吗?
对于某些需要支持某些查询模式的索引,当然可以。
但是要对所有索引执行此操作,我肯定会说不。
在实际上不需要的地方做会浪费空间,并且会显着减慢插入/更新速度。它可能会减慢尽可能多的读取查询,因为每个索引页面包含的记录较少,因此任何需要引用索引块进行过滤但不使用所有其他列的查询都必须访问更多页面。这将使您的数据库更需要内存:这些页面需要加载到缓冲池中,如果内存不足,可能会弹出其他有用的页面。如果对这些索引使用压缩来尝试减轻对存储和内存要求的影响,那么它将向 CPU 推送额外负载。
因为访问是通过 ORM 默认(但不总是)检索所有列
这是使用 ORM(或只是简单的 ORM)优化不佳的常见模式,在这些情况下,我已经看到 SQL Server 的索引顾问(和类似的 3rd 方工具)建议使用许多INCLUDE
d 列的索引,所以我同意你的建议这就是索引以这种方式创建的原因。
但是,虽然它可能使所有此类查询稍微快一些,其中一些查询速度明显更快,但我怀疑在许多情况下,任何好处都太小了,以至于不值得您的常用工作集、磁盘空间和磁盘空间所需的额外内存占用磁盘和内存之间的IO。
还请记住,ORM 可能不会选择查询涉及的所有表的所有列,因此该好处可能仅适用于当前请求的主要目标,并且当其他对象用于过滤时,较大的索引可能会惩罚查询但不返回数据(SELECT * FROM table1 WHERE id IN (SELECT someID FROM table2 WHERE someColumn='DesiredValue')
也许)。
额外空间使用的另一个考虑因素,特别是当数据很大时,它会对您的备份策略产生影响:这些备份的存储和传输成本、潜在的恢复时间等等。
我们是否应该为两者之间的任何差异做好准备 [on-prem & AzureSQL]
一般来说,我认为这里的注意事项在每种情况下都是相同的,尽管大型索引造成的任何额外内存/IO 成本可能在 Azure 中更直接可见,您可以在 Azure 中调整服务层,因此基础设施成本更容易而不是拥有一套相对固定的硬件资源。如果使用标准/高级层而不是基于 vcore 的定价,那么您将受到标准 IO 成本的更大影响,因为高级包括每个 DTU 显着更多的 IO。如果您在 Azure 中使用多区域备份或冗余或其他非本地功能,则可能会产生与非正常宽索引占用的额外空间相关的带宽成本。
问题是,这是一个明智的策略吗?....(尽管有人提出了一个问题,如果这个策略是最优的,它现在不是默认的吗?)
在大多数情况下,这不是一个明智的策略。原因是,在一般 OLTP 数据库中,返回给最终用户的行不会很多。(概括)
您应该问自己的问题是,如果您在关键列上查找,该查找操作将返回多少行?并对在该列上搜索的查询重复此操作。
考虑下表,返回大量列, where SelectiveIDField= ...
select columnA,columnC, ... columnZ
FROM dbo.BigTable
Where SelectiveIDField= '225122141';
Run Code Online (Sandbox Code Playgroud)
如果在上的selectiveIDField
查找只会返回一行,那么额外的键查找是一件坏事吗?
(猜你这里有聚集索引,否则 RID 查找)
它只会做一次额外的键查找,一次额外的执行 + 连接运算符。就算是10个甚至100个,会不会有那么大的影响?这还取决于您的查询执行了多少以及执行时间的重要性。
在它可以忽略不计的情况下,只需创建索引SelectiveIDField
并称之为一天,与写入损失相比,读取收益不值得。
所以简而言之,在我看来,在整个表上创建索引不应该是默认方法,除非您真的看到查询的问题并且可以通过添加整个覆盖索引来大幅改进它。