0

我有一个名为 readings 的表,其中包含 > 7600 万行,我正在运行此查询:

declare @tunnel_id int = 13
SELECT TOP 1 local_time, recorded_time
FROM readings
WHERE tunnel_id = @tunnel_id 
ORDER BY id DESC

id列是bigint,设置为主键,有聚集索引,在tunnel_id字段上也有索引。

我正在尝试的 20 个不同的 tunnel_id 中的大约 16 个在不到一秒的时间内返回效果很好。但是,在最后 4 左右,查询需要 40 秒并使用数十万次读取。

我尝试将查询修改为:

SELECT TOP (1) local_time, recorded_time
FROM readings
where id = (
    SELECT TOP 1 id
    FROM readings
    WHERE tunnel_id = 13
    ORDER BY id DESC
)

对于一些隧道 ID 来说,这又一次很慢。更让我感到困惑的是,内部选择对于慢 id 的运行速度很快,如果我硬编码最大 id 而不是子查询,它也运行得很快。

我在这里缺少什么导致此查询执行不佳?

编辑评论:

Tunnel_id 不是唯一的,每个隧道都有数百万行。这是在 Sql Server 2012 上运行的。

我包括了快速和慢速运行的实际执行计划,它们是相同的。

快速地: sql快速执行计划

慢的:

sql快速执行计划

但正如您所看到的,第一个执行不到一秒,而第二个需要 51 秒。

4

3 回答 3

1

该计划基本上是从头到尾扫描整个聚集索引,并查找具有 tunnel_id = @tunnel_id 的第一行。

我有根据的猜测是,“慢”隧道在聚集索引的开头没有任何行,因此它必须扫描更多。

这个非聚集索引应该加快速度:

CREATE NONCLUSTERED INDEX [IX_FOO] ON [readings]
(
    tunnel_id, 
    ID 
)
INCLUDE 
(
    local_time, 
    recorded_time
)

这可以替换 tunnel_id 上的现有索引。

于 2013-07-09T20:51:38.610 回答
1

这里有趣的部分是 SQL 根本不使用 tunnel_id 中的索引,而只是扫描整个表,如果它像 7600 万行这样大,这会很慢。我认为它没有使用它的真正原因是因为按 id 排序,因为它必须执行查找然后进行额外的排序。起初我怀疑参数嗅探是这里的主要问题。

我会尝试更改索引,并使其覆盖。如果可能的话,在索引中包括本地时间、记录时间和 id(不是 100% 确定是否需要它,因为它无论如何都是集群键)。

CREATE NONCLUSTERED INDEX IX_tunnel_id ON dbo.readings (tunnel_id) INCLUDE (id, local_time, recorded_time)

请注意,虽然这可以改进此特定查询,但它会使插入和更新速度变慢,并且需要额外的存储空间。

于 2013-07-09T20:52:55.253 回答
0

刚刚发现可以提示使用 tunnel_id 索引:

declare @tunnel_id int = 13
SELECT TOP 1 local_time, recorded_time
FROM readings
WITH (INDEX(idx_tunnel_id))
WHERE tunnel_id = @tunnel_id 
ORDER BY id DESC

它按预期工作并在不到 1 秒内返回。

于 2013-08-19T20:45:34.403 回答