【问题标题】:Is this a bad indexing strategy for a table?这是一个糟糕的表索引策略吗?
【发布时间】:2010-10-07 18:40:44
【问题描述】:

有问题的表是供应商软件在我们网络上使用的数据库的一部分。该表包含有关文件的元数据。表的schema如下

Metadata 
ResultID (PK, int, not null) 
MappedFieldname (char(50), not null) 
Fieldname (PK, char(50), not null) 
Fieldvalue (text, null)

ResultID 和 Fieldname 上有一个聚集索引。该表通常包含数百万行(在一种情况下,它包含 5 亿行)。该表由 24 个工作人员填充,每个工作人员在“处理”数据时运行 4 个线程。这会导致许多非顺序插入。稍后在处理后,我们的一些内部软件将更多数据插入到此表中。给定表的碎片至少为 50%。对于最大的表,它是 90%。我们没有 DBA。我知道我们迫切需要一个数据库维护策略。就我的背景而言,我是一名在这家公司兼职的大学生。

我的问题是,聚集索引是解决此问题的最佳方式吗?是否应该考虑另一个索引?对于这种类型和类似的临时 DBA 任务,是否有任何好的参考资料?

【问题讨论】:

    标签: database sql-server-2005 clustered-index


    【解决方案1】:

    索引策略完全取决于您如何查询表以及从各个查询中获得多少性能。

    当进行无序插入时(这称为“页面拆分”),聚集索引可以强制对行进行物理重新排序(在磁盘上)。在索引页上没有可用空间的大表中,这可能需要一些时间。

    如果您不是绝对要求有一个跨越两个字段的聚集索引,那么不要。如果它更像是一种 UNIQUE 约束,那么一定要把它变成一个 UNIQUE 约束。这些不需要重新排序。

    确定针对表的典型查询是什么,并相应地放置索引。您拥有的索引越多,数据更改 (INSERTs/UPDATEs/DELETEs) 的速度就越慢。不要创建太多索引,例如在不太可能被过滤/排序的字段上。

    通常只在一起过滤/排序的字段上创建组合索引。

    【讨论】:

    • 典型的查询将是非顺序插入、不更新、不删除,并且选择不如插入频繁(写多,读少)。我想我需要阅读并查看定期运行的查询。
    • 这听起来像是删除聚集索引的好方案。另外,查看索引填充因子。确保有足够的空间来减少进行索引页面拆分的需要。默认填充因子 80 可能对您的需求来说太高了。
    【解决方案2】:

    仔细查看您的查询 - 那些命中表格以获取数据的查询。该指数会服务吗?如果您在 (ResultID, FieldName) 上有一个按该顺序排列的索引,但您正在查询给定 Fieldname 的可能 ResultID 值,则 DBMS 很可能会忽略该索引。相比之下,如果您在 (FieldName, ResultID) 上有一个索引,它可能会使用该索引 - 当然用于简单的值查找 (WHERE FieldName = 'abc')。就唯一性而言,任一索引都运行良好;在查询优化方面,(至少可能)存在巨大差异。

    使用EXPLAIN 查看您的查询是如何被您的 DBMS 处理的。

    聚集索引与非聚集索引通常是 DBMS 中的二阶优化效果。如果您的索引正确,则聚集索引和非聚集索引之间存在微小差异(聚集索引的更新惩罚更大,作为对稍小的选择时间的补偿)。在担心二阶效应之前,请确保其他所有内容都已优化。

    【讨论】:

    • +1 因为他似乎担心的是 INSERT/UPDATE 性能而不是 SELECT 性能,集群/非集群可能是他的某种一级优化。
    【解决方案3】:

    就我所见,聚集索引还可以。关于其他索引,您需要提供对该表进行操作的典型 SQL 查询。只是突然创建一个索引绝不是一个好主意。 您在谈论碎片和索引,这是否意味着您怀疑查询执行速度变慢?或者您只是想缩小/整理数据库/索引?

    在非工作时间不时对索引进行碎片整理是个好主意,但您必须考虑到频繁/随机插入时,在表中留出一些空闲空间以防止页面分裂(确实会影响性能)。

    【讨论】:

      【解决方案4】:

      我知道我们迫切需要数据库维护策略。

      +1 用于确定需求

      就我的背景而言,我是一名在这家公司兼职的大学生

      继续学习,积累经验,同时请一位经验丰富的顾问。

      该表由 24 个工作人员组成,每个工作人员运行 4 个线程

      我认为这在工作日是非常关键的任务,而停机是坏消息?如果是这样,请不要附和它。

      ResultID 和 Fieldname 上有一个聚集索引

      如您所说,ResultID 是 PK 中的第一列吗?

      如果是这样,我敢打赌,它的选择性不够,并且根据查询的需求,应该交换 PK 字段的顺序(尽管这个复合键看起来对于集群来说是一个糟糕的选择PK)

      结果是什么:

      从 MyTable 中选择 COUNT(*)、COUNT(DISTINCT ResultID)

      如果第一个计数是第二个计数的 4 倍或更多,由于 ResultsID 的选择性低,您很可能会优先获得扫描而不是搜索,并且一些简单的更改将提供巨大的性能改进。

      此外,字段名非常宽(50 个字符),因此任何二级索引都会在每个索引条目中添加 50 + 4 个字节。这些字段真的是 CHAR 而不是 VARCHAR 吗?

      我个人会考虑增加叶页的密度。在 90% 时,您只会留下一些空白 - 可能每页一个。但是对于一个包含 5 亿行的大表,更高的打包密度可能意味着树中的级别更少,因此检索的次数更少。与此相反,对于给定的页面,几乎每个插入都需要页面拆分。这将有利于集群的插入,因此可能不合适(假设您的插入数据可能未集群)。像许多事情一样,您需要进行测试以确定哪种索引键密度最有效。 SQL Server 有一些工具可以帮助分析查询是如何被解析的、它们是否被缓存、它们导致的表扫描次数、哪些查询“运行缓慢”等等。

      请一位顾问来看看并给你一些建议。这不是一个在这里回答的问题将为您提供一个安全的解决方案来实施。

      您确实需要仔细考虑维护策略,以维护每天有 5 亿行和大量插入的表。抱歉,我对进入这种状态的公司感到非常沮丧。

      该表需要进行碎片整理(如果您没有聚集索引,您的选择会变得更少,因此请保留它,直到您确定有更好的候选者)。 “在线”碎片整理方法将对性能产生适度的影响,并且可能会突然消失 - 如果它们超出时间/CPU限制,可以安全地中止[尽管这很可能需要一些编程]。如果您有一个“安静”插槽,则将其用于表碎片整理和更新索引的统计信息。不要等到周末才尝试一次完成所有桌子 - 在每天的任何安静时间(大概在晚上)尽可能多地做。

      对表进行碎片整理可能会导致 Transaction log 使用量大幅增加,因此请确保经常备份任何 TLog(我们有一个 10 分钟的 TLog 备份策略,我们在表碎片整理期间将其增加到每分钟一次,以便碎片整理过程不会成为所需 Tlog 空间的定义!)

      【讨论】:

        猜你喜欢
        • 2011-01-13
        • 1970-01-01
        • 2014-09-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-06
        • 2011-08-01
        相关资源
        最近更新 更多