0

注意:这是从 Stack Overflow Teams 网站重新发布的一个问题,以吸引更广泛的受众

我有一个包含数百万条记录的事务日志表。链接到这些日志的许多数据项的每个项可能有超过 100K 的行。

如果用户在日志表中存在超过 1000 个项目时尝试删除项目,我需要显示警告。

我们已确定 1000 条日志表示该项目正在使用中

如果我尝试简单地查询表以查找日志行的总数,则查询执行时间太长:

SELECT COUNT(1) 
FROM History
WHERE SensorID IN (SELECT Id FROM Sensor WHERE DeviceId = 96)

从历史记录表计数需要太长时间

是否有更快的方法来确定实体是否有超过 1000 条日志记录?

注意:历史表在SensorId列上有一个索引。

4

1 回答 1

3

使用 Count 而不是返回所有行并检查记录计数是正确的,但我们仍然要求数据库引擎在所有行中查找。

如果要求不是返回最大行数,而只是确定是否有超过X行,那么我要做的第一个改进是返回表中前X行的计数。

所以如果X是 1000,你的应用逻辑不需要改变,你仍然可以确定一个有 999 个日志的项目和 1000+ 个日志的区别

我们只需更改现有查询,选择TOP(X)行而不是计数,然后返回该结果集的计数,只选择主键或唯一索引列,这样我们只检查索引而不是底层表存储。

select count(Id) FROM (
  SELECT TOP(1000)    // limit the seek that the DB engine does to the limit
         Id           // Further constrain the seek to just the indexed column 
  FROM History 
  where SensorId IN ( // this is the same filter condition as before, just re-formatted
    SELECT Id 
    FROM Sensor 
    WHERE DeviceId = 96)
) as trunk

将此查询更改为前 10,000 仍可提供亚秒级响应,但是在X = 100,000 时,查询所用的时间几乎与原始查询一样长


如果有问题的表具有高事务率并且执行时间的主要原因是由于锁争用导致的等待,那么对于此类问题还有另一种看似“灵丹妙药”的方法。

如果您怀疑锁定是问题所在,并且您可以接受包含未提交行的计数响应,那么您可以使用WITH(NOLOCK)表提示来允许查询在READ UNCOMMITED事务隔离级别中有效运行。

这里有一个关于NOLOCK 表提示对选择查询的影响的很好的讨论

SELECT COUNT(1) FROM History WITH (NOLOCK) 
WHERE SensorId IN (SELECT Id FROM Sensor WHERE DeviceId = 96)

尽管强烈反对,但这是一个很好的例子,说明 NOLOCK 很容易被允许,它甚至是有道理的,因为您在删除之前的计数将考虑另一个用户或正在积极添加到日志计数的操作。

经过多次试验,当查询 1000 或 10K 行时,使用计数的选择解决方案仍然比使用NOLOCK表提示更快。NOLOCK然而,提供了以最小的更改执行相同查询的机会,同时仍能及时返回。

随着基础结果集中行数的增加, select with 的性能NOLOCK仍然会增加,而当超过 top 限制时,具有 top而没有 order by 子句的 select 的性能应该保持不变。

于 2018-09-06T15:54:53.183 回答