【问题标题】:Update table with index is too slow使用索引更新表太慢
【发布时间】:2010-03-26 15:06:41
【问题描述】:

我在我们的应用程序的实时系统上观察 Profiler,我看到有一条更新指令,我们定期(每秒)运行,速度很慢。每次大约需要400毫秒。 查询包括此更新(这是缓慢的部分)

UPDATE BufferTable
    SET LrbCount = LrbCount + 1,
    LrbUpdated = getdate()
WHERE LrbId = @LrbId

这是桌子

CREATE TABLE BufferTable(
    LrbId [bigint] IDENTITY(1,1) NOT NULL,
        ...
    LrbInserted [datetime] NOT NULL,
    LrbProcessed [bit] NOT NULL,
    LrbUpdated [datetime] NOT NULL,
    LrbCount [tinyint] NOT NULL,
)

该表有 2 个索引(非唯一且非聚集),字段按以下顺序排列:
* Index1 - (LrbProcessed, LrbCount)
* Index2 - (LrbInserted, LrbCount, LrbProcessed)

当我看到这个时,我认为问题来自 Index1,因为 LrbCount 发生了很大变化,它改变了索引中数据的顺序。
但在停用 index1 后,我看到查询的时间与最初相同。 然后我重建index1并停用index2,这次查询非常快。

在我看来 Index2 应该更新得更快,数据的顺序不应该改变,因为 LrbInserted 时间没有改变。

有人能解释一下为什么 index2index1 更新更重吗?

谢谢!

编辑

我刚刚意识到我假设错了。
完整的查询还有另一部分负责延迟:

DECLARE @LrbId as bigint
SELECT TOP 1 @LrbId = LrbId
FROM Buffertable
WHERE LrbProcessed = 0
    AND LrbCount < 5
ORDER BY LrbInserted

因此,这很可能与 Sql 引擎对使用哪个索引的错误决定有关。
对困惑感到抱歉。我想我们可以结束这个问题了。

【问题讨论】:

    标签: sql-server performance indexing


    【解决方案1】:

    有人能解释一下为什么 index2 比 index1 更难更新吗?

    index2 更长:密钥大小为 10 字节 (8 + 1 + 1) 而不是 2 (1 + 1)

    可能它不适合缓存,需要进行页面查找来定位记录。

    你的桌子有多大?

    您可能还想启用 I/O 统计:

    SET STATISTICS IO ON
    

    ,运行几次查询,查看输出中的物理页读取次数。

    更新:

    对于这个查询:

    SELECT TOP 1 @LrbId = LrbId
    FROM   Buffertable
    WHERE  LrbProcessed = 0
           AND LrbCount < 5
    ORDER BY
           LrbInserted
    

    要快速工作,请创建以下索引:

    CREATE INDEX ix_buffertable_p_c_i ON BufferTable (LrbProcessed, lrbCount, LrbInserted)
    

    并重写查询:

    WITH    cts (cnt) AS
            (
            SELECT  1
            UNION ALL
            SELECT  cnt + 1
            FROM    cts
            WHERE   cnt < 5
            )
    SELECT  TOP 1 bt.*
    FROM    cts
    CROSS APPLY
            (
            SELECT  TOP 1 bti.*
            FROM    BufferTable bti
            WHERE   LrbProcessed = 0
                    AND LrbCount = cts.cnt
            ORDER BY
                    LrbInserted
            ) bt
    ORDER BY
            LrbInserted
    

    【讨论】:

    • ...不要忘记定期更新表统计信息,否则优化器可能会根据过时的信息选择效率较低的路径。这对于快速增长或营业额高的数据库非常重要。
    • 我对这个假设完全错误,(我编辑了这个问题)。但更新统计数据可能很重要。
    • @Quassnoi,顺便说一下,表格有 833020 行。
    • @paulya:查看答案更新。顺便说一句,LrbProcessed = 0 AND LrbCount &lt; 5 有多少条记录?
    【解决方案2】:

    LrbId 上有索引,还是主键?如果没有,添加一个应该会总体上改进更新。

    请注意,如果您在不同的会话中获得多个更新,则在更新修改索引时可能会出现一些并发问题。

    如 Quassnoi 所述,索引 2 也需要更新。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-20
      • 1970-01-01
      • 2011-09-07
      • 1970-01-01
      • 2021-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多