SELECT TOP 1 from a very large table on a index column 非常慢,但不是反向顺序(“desc”)

Tif*_*nyP 17 performance sql-server select sql-server-2014 top query-performance

我们有一个大约 1TB 的大型数据库,在强大的服务器上运行 SQL Server 2014。几年来,一切都运行良好。大约 2 周前,我们进行了全面维护,其中包括: 安装所有软件更新;重建所有索引和压缩 DB 文件。但是,我们没想到在某个阶段,在实际负载相同的情况下,DB 的 CPU 使用率会增加超过 100% 到 150%。

经过大量的故障排除,我们将其缩小到一个非常简单的查询,但我们找不到解决方案。查询非常简单:

select top 1 EventID from EventLog with (nolock) order by EventID
Run Code Online (Sandbox Code Playgroud)

它总是需要大约 1.5 秒!但是,使用“desc”的类似查询总是需要大约 0 毫秒:

select top 1 EventID from EventLog with (nolock) order by EventID desc
Run Code Online (Sandbox Code Playgroud)

PTable 大约有 5 亿行;EventIDASC数据类型为 bigint(身份列)的主聚集索引列(ordered )。顶部有多个线程向表中插入数据(较大的 EventID),底部有 1 个线程删除数据(较小的 EventID)。

在 SMSS 中,我们验证了两个查询始终使用相同的执行计划:

  • 聚集索引扫描;

  • 估计行数和实际行数均为1;

  • 估计和实际执行次数均为1;

  • 估计I/O成本是8500(好像有点高)

  • 如果连续运行,则两者的查询成本相同 50%。

我更新了索引统计with fullscan,问题依旧;我再次重建索引,问题似乎消失了半天,但又回来了。

我打开了 IO 统计:

set statistics io on
Run Code Online (Sandbox Code Playgroud)

然后连续运行两个查询并找到以下信息:

(对于第一个查询,较慢的)

表'PTable'。扫描计数1,逻辑读407670,物理读0,预读0,lob逻辑读0,lob物理读0,lob预读0。

(对于第二个查询,快速的一个)

表'PTable'。扫描计数 1,逻辑读取 4,物理读取 0,预读读取 0,lob 逻辑读取 0,lob 物理读取 0,lob 预读读取 0。

请注意逻辑读取的巨大差异。在这两种情况下都使用索引。

索引碎片可以解释一点,但我认为影响很小;而且这个问题以前从未发生过。另一个证明是,如果我运行如下查询:

select * from EventLog with (nolock) where EventID=xxxx   
Run Code Online (Sandbox Code Playgroud)

即使我将 xxxx 设置为表中最小的 EventID,查询也总是闪电般快速。

我们检查过,没有锁定/阻塞问题。

注意:我只是试图简化上面的问题。“PTable”实际上是“EventLog”;的PIDEventID

我在没有NOLOCK提示的情况下得到相同的结果测试。

有人可以帮忙吗?

在此处输入图片说明

在此处输入图片说明

更详细的 XML 查询执行计划如下:

https://www.brentozar.com/pastetheplan/?id=SJ3eiVnob

https://www.brentozar.com/pastetheplan/?id=r1rOjVhoZ

我认为提供 create table 语句并不重要。这是一个旧数据库,在维护之前一直运行良好。我们自己做了很多研究,并将其范围缩小到我的问题中提供的信息。

该表通常是以EventID列作为主键创建的,它是一个identity类型为 的列bigint。这时候,我猜测问题出在索引碎片上。索引重建后,问题似乎消失了半天;但是为什么这么快就回来了……?

Pau*_*ite 18

聚集索引扫描显示 423,723 次逻辑读取返回第一行,耗时 1926 毫秒:

坚果

按索引顺序定位第一行似乎很多。

很可能您的幽灵清理任务运行很远,或者已经停止。您应该检查ghost_record_count聚集索引sys.dm_db_index_physical_stats并监视随时间的变化。

从索引末尾开始的有序扫描看到了不断的删除活动,在它找到要返回的第一个“活动”行之前,必须扫描大量的幻影记录。这解释了额外的逻辑读取。向下搜索 b 树到索引的最低值将遇到更少的幻影记录。

另一个影响性能的因素是扫描本身负责删除存储引擎内部: Paul Randal 的深度幽灵清理中提到的幽灵记录。

您应该检查跟踪标志 661(禁用重影清除)是否处于活动状态。

解决方案

如果 Ghost 清理过程已完全停止,最有效的解决方案通常是重新启动 SQL Server 实例。您还应该确保 SQL Server 正在运行最新的累积更新之一。多年来,出现了许多幽灵清理错误。

在您的具体情况下:

原来问题是由同一台服务器上的另一个测试数据库引起的。该测试数据库因“数据丢失”而恢复,并且已损坏。令人惊讶的是,幽灵清理过程显然卡在了该数据库中。一旦我们从 SMSS 中删除了损坏的数据库,问题就自行解决了(花费了很长时间,可能导致 DB 暂时锁定)。