用过滤的(非空值)索引替换索引有什么作用?


10

我们的项目运行着非常大,非常复杂的数据库。因此,大约一个月前,我们注意到包含空值的索引列使用的空间变得太大。为此,我编写了一个脚本,该脚本将动态搜索包含大于1%的空值的所有单列索引,然后在值不为空的情况下删除并重新创建这些索引作为筛选索引。这将在整个数据库中删除并重新创建数百个索引,通常会释放整个数据库使用的近15%的空间。

现在我对此有两个问题:

A)以这种方式使用过滤索引的不利之处是什么?我认为这只会提高性能,但是会涉及性能风险吗?

B)在删除和重新创建索引时,我们收到错误消息(“无法删除索引XYZ,因为它不存在或您没有权限”),即使事后检查,一切都按预期进行。怎么会这样

谢谢你的帮助!

编辑:响应@Thomas Kejser

嗨,谢谢,但事实证明这是一场灾难。当时我们还不了解几件事,例如:

  1. 在查询期间,SQLOS在确定不能使用NULL值联接表列之前先制定索引计划。IE,您确实确实需要有WHERE子句过滤器,以适合查询中使用的每个过滤索引的索引,否则该索引将根本不使用。
  2. 删除和创建索引,然后再次冗余更新其统计信息可能仍然不足以产生更新的计划,而我们认为是可以的。在某些情况下,似乎只有足够高的工作量会迫使SQL Server重新评估计划。
  3. 执行计划程序的功能有些奇特之处,仅凭常识和逻辑很难确定。甚至在成千上万的由代码背后生成的不同查询的变体中,看似无用的索引也可以帮助进行某些统计和查询计划,最终将其用于关键查询中。

最后,这些更改被还原。因此,过滤后的索引是一个强大的工具,但是您需要真正了解从这些列中获取的数据。除了空间问题以外的常规索引都非常容易应用,而过滤后的索引则代表了非常定制的解决方案。它们当然不能代替常规索引,而是在需要它们的特殊情况下对其的扩展。


您可能还需要重新检查索引策略。如果您有数百个单字段索引,则可能不是最佳选择。
JNK 2013年

这些需求来自数据库是部分继承自另一个系统的事实。默认情况下,我们有一些抽象表和几个根本不使用的抽象列,这些抽象列将产生大量的大量索引NULL值。至于单字段索引,它们是根据对每个外键都应建立索引的基本要求创建的,其中许多都在这些列中,这些列主要包含或仅包含NULL值。
卡恩

Answers:


8

非常有趣的方法。我对创造力的支持。

既然您已经回收了空间,那么我认为原始索引不再存在了吗?过滤索引的缺点是:

实际上,这意味着您必须格外小心筛选索引,因为它们通常会导致可怕的查询计划。我不会称它们为无用的,但是我将它们视为传统索引的补充,而不是替代(正如您正在尝试的那样)。


“查询参数化不适用于过滤的索引”。这大概可以固定选项(重新编译)
MichaelD

2

Thomas Kejser在上面很好地回答了这个话题

我只是想加2美分。

当您将查询中的where子句与过滤索引中的where完全匹配时,我已经看到一些过滤索引仅在使用(显示在执行计划中)。

您是否尝试过使用索引视图稀疏列

我相信,只要您只有内部关节,就可以创建一个包含已过滤索引的where子句的索引视图,然后可以使用该视图。

可能有不止一种观点。但是与非聚集索引一样,太多索引会减慢记录速度。

以我的经验,您将在阅读方面有很好的收获,但是如果表涉及复制,则必须监视写入(插入和更新)。

但是,据我所知,the null values因此我主要建议您在index中的SPARSE列

稀疏列特别适合于过滤索引

正如我宣传的稀疏列一样,如果我也没有告诉您它的局限性,我会感到不舒服:

当设计具有稀疏列的表时,请记住,更新一行时,表中的每个非空稀疏列都需要额外的2个字节的开销。

结果,

额外的内存需求,当总行大小(包括此内存开销)超过8019时,更新可能会意外失败并出现错误576,

并且没有列可以从行中推出。

考虑一个表的示例,该表具有600个类型为bigint的稀疏列。

如果有571个非空列,则磁盘上的总大小为571 * 12 = 6852字节。在包括额外的行开销和稀疏列标题之后,这增加到大约6895字节。该页在磁盘上仍有大约1124字节可用。这给人的印象是其他列可以成功更新。但是,在更新过程中,内存中的额外开销为2 *(非空稀疏列的数量)。在此示例中,包括额外的开销– 2 * 571 = 1142字节–将磁盘上的行大小增加到大约8037字节。该大小超过了最大允许的8019字节大小。由于所有列都是固定长度的数据类型,因此无法将其从行中推出。结果,更新失败并显示576错误。

有关以上链接的更多详细信息,但是我也希望在此发布此警告:

将列从稀疏更改为稀疏或从稀疏更改为稀疏需要更改列的存储格式。

SQL Server数据库引擎使用以下过程来完成此更改:

1-以新的存储大小和格式将新列添加到表中。

2-对于表中的每一行,更新并将旧列中存储的值复制到新列中。

3-从表架构中删除旧列。

4-重建表(如果没有聚集索引)或重建聚集索引以回收旧列使用的空间。


1
你好 争论还很晚,但是可以,虽然我们很久以前就放弃了本主题中描述的方法,但最近我们确实选择了一种更具选择性的方法。基本上,我们查看了统计信息的使用情况和业务模型,以在每个表的基础上确认索引。然后通过在正常索引旁边添加新的过滤索引进行测试,并检查了几周后最终使用了哪个索引。在确认仅在新计划中使用了过滤索引之后,我们删除了正常的未过滤索引。
卡恩

1
另外,我们确实更改了很多列来稀疏类型。但是,这样做的问题是,如您在MSDN中所看到的,将列类型更改为稀疏基本上会强制重新创建整个聚集索引。这对于大型,复杂的表而言相当繁重。因此,我们重命名了约束和表,创建了一个具有相同模型和原始名称但列稀疏的新表,然后以适当的批次将数据传输到新表中。然后检查完​​一切正常,所有索引和FK都重新放置到位,删除旧表。
卡恩

1
另外,在某些情况下,使用页面压缩是更可取的,因此我们最终改为使用页面压缩。这也很方便,因为您可以简单地使用DROP_EXISTING = ON创建现有的聚集索引,以使其比稀疏路由快得多,快得多。特别是因为它避免了重新管理索引和FK的整个麻烦。
卡恩
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.