【问题标题】:Query performance, indexing, and prediction of write time performance hit of a covered index?覆盖索引的查询性能、索引和写入时间性能命中预测?
【发布时间】:2013-10-28 14:11:26
【问题描述】:

约束

我目前无法更改查询,因为它是由应用程序动态构建的,我们无法在今天、本周甚至本月将代码推送到 PROD 并进行修复。这必须在数据库中解决。这就是我评估索引的原因。

我们的数据库中有一个表 CaseHistory,它有大约 10MM 行。不可怕,但这是一种成长的痛苦。读取时间开始受到来自如下搜索的查询的影响:

select CaseNumber
    ,isnull(
        (
            select convert(varchar,min(CreationTimeGMT),101)
            from CaseHistory
            where CaseNumber = c.CaseNumber
                and ActionTypeID = 1
        ), 'N/A'
    ) as CreationTimeGMT
    ...
from [Case] c
where CaseNumber in (
    select CaseNumber from CaseHistory
    where ActionTypeID <> 1 and
        CreationTimeGMT >= '10/25/2013'
    ) AND
    CaseNumber in (
        select CaseNumber from CaseHistory
        where ActionTypeID <> 1 and
            CreationTimeGMT <= '10/25/2013'
    )

现在,乍一看可能会认为获取CreateionTimeGMT 的子查询可能是个问题,但我不这么认为,因为我已经分析了执行计划。此查询的执行计划针对IX_CaseHistory_1 使用了SEEK 上99% 的处理(在当前索引 中显示)。为了进一步具体化我不认为是子查询的原因,直接搜索CaseNumber,如下所示:

select CaseNumber
    ,isnull(
        (
            select convert(varchar,min(CreationTimeGMT),101)
            from CaseHistory
            where CaseNumber = c.CaseNumber
                and ActionTypeID = 1
        ), 'N/A'
    ) as CreationTimeGMT
    ...
from [Case] c
where CaseNumber = '123456'

sub 1s,而上述查询在 13s15s 之间运行。

当前索引

IX_CaseHistory (CaseNumber (ASC))
IX_CaseHistory_1 (ActionTypeID (ASC))
IX_CaseHistory_2 (CreationTimeGMT (ASC))

所以,我想做的是在CaseNumber, ActionTypeID, CreationTimeGMT 上构建一个集群 覆盖索引。目前聚集索引位于IDENTITY PK

为什么要集群?

因为我也希望这个查询运行得更快(每天执行 1000 次):

select  CaseHistoryID
    ,CaseNumber
    ,ActionTypeID
    ,CreationTimeGMT
    ,UserID
    ,Notes
from    CaseHistory
where   CaseNumber = @CaseNumber
order by CreationTimeGMT

但是,我有一个基本问题,我如何预测这会对写入时间产生什么样的影响?

【问题讨论】:

  • +1 提出一个简洁的问题
  • 能否添加相关的数据库标签?
  • 我们需要查看查询计划来评估您的分析。
  • 也许将您的两个 IN 更改为一个查询可能会使事情变得更快:从 CaseHistory 中选择 CaseNumber 其中 ActionTypeID 1 group by CaseNumber 具有 min(CreationTimeGMT) '10/25/2013'
  • @the_lotus,是的,这是保证。我添加了一个事实,即我不能将查询更改为约束。

标签: sql sql-server performance sql-server-2008-r2


【解决方案1】:

我如何预测这会对写入时间产生什么样的影响?

对于插入(我假设这就是您所说的“写入”),使用聚集索引时的主要问题是新数据将插入到哪里。如果您通常将值添加到聚集索引的 end(例如 Auto-Increment 键),那么写入应该非常快 - 它只是将新记录添加到末尾。

在您的情况下,我假设插入是不是连续的,而是随机放置在现有数据中。在这种情况下,您需要考虑fill factor,这将决定现有记录之间留出多少空间来接受插入。

低填充因子以允许多次插入的代价是非索引列的读取时间更长,因为结果数据可能分布在多个页面上,因此需要更多 I/O。还需要更多磁盘空间,因为表需要为新插入分配空白空间(而不仅仅是自动增长)

我会将您的填充因子降低到 80(意味着为新插入留出 20% 的空间)并定期重新组织您的表,以便在记录之间为新数据保留一些空间。

【讨论】:

  • 是的,你说得对,write 我的意思是INSERT。因此,如果我离开 current clustered index(位于 IDENTITY 列上)并添加新覆盖的索引,我不会真正看到 write 的增加次?在写入期间重建新覆盖索引的开销是否不足以衡量?
  • 你只能有一个聚集索引,所以你必须替换当前的聚集索引。
  • 明白,但如果我只是将其保留在当前配置中,则写入数据页不会产生真正的开销——但是当添加了新记录?
  • 使用当前的聚集索引,你总是写到最后,速度很快,但读取受到影响,因为它必须查找键值然后去读取数据。更新新的覆盖索引将取决于填充因子。
【解决方案2】:

你最好从头开始修改你的 sql,

SELECT
        c.[CaseNumber],
        isnull(convert(varchar, min(h.[CreationTimeGMT]), 101), 'N/A'),
        ...
FROM [Case] c
LEFT JOIN [CaseHistory] h ON h.[CaseNumber] = c.[CaseNumber]
GROUP BY
        c.[CaseNumber]
WHERE
        h.[ActionTypeID] = 1
    AND
        EXISTS(
            SELECT
                    h.[CaseNumber]
            FROM [CaseHistory] h
            WHERE
                    h.[CaseNumber] = c.[CaseNumber]
                AND
                    h.[ActionTypeID] <> 1
                AND
                    h.[CreationTimeGMT] BETWEEN '10/25/2013' AND '10/25/2013');

一旦你这样做了,你可以看到 where 子句中的子查询(ies/y)是一个更复杂的命题。

我怀疑对于CaseHistory,您的聚集索引应该保留在CaseHistoryID 上,因为它是独一无二的。我很想在

上创建一个覆盖索引
`CaseNumber`, `ActionType`, `CreationTimeGMT`

但是,由于子查询中的“&lt;&gt; 1”,我也会尝试翻转条件,例如

                    h.[CreationTimeGMT] BETWEEN '10/25/2013' AND '10/25/2013'
                AND
                    h.[ActionTypeID] <> 1);

并添加此覆盖索引

`CaseNumber`, `CreationTimeGMT`, `ActionType`

与以往一样,性能的关键是首先获得最具选择性的条件。

我无法预测您数据库的实际成本,因为我没有您的数据、统计数据、环境等...

【讨论】:

  • +1 因为我不同意这个查询很糟糕。事实上,我们有一个 CC 来修复它是如何工作的,我会在重建该代码时考虑你的方法。现在,至于涵盖的索引,我实际上是在通过类似的想法(将ActionType 添加到它的末尾),然后将修改查询以利用BETWEENAND,就像我在可以重建它。现在,您也会同意覆盖索引至少会改变查询所需的时间,对吧? 预测该新索引的写入时间开销如何?
  • @neoistheone,真实但可能微不足道。您多久插入一次行?
  • 这可能是针对该表的事务负载的约 35%。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-25
  • 2015-11-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多