索引日期时间字段上的计数(*)过滤运行时间过长

时间:2016-11-22 18:09:22

标签: postgresql

我正在尝试计算昨天创建的所有记录。有一个 created_at 列,它已编入索引。

如果我跑

explain
select count(*) from events where created_at::date = current_date - 1;

它说

Aggregate  (cost=14365728.05..14365728.06 rows=1 width=0)
  ->  Index Only Scan using index_events_created_at on events  (cost=0.57..14362310.20 rows=1367140 width=0)
        Filter: ((created_at)::date = (('now'::cstring)::date - 1))

所以事件有点知道有多少行。但是

select count(*) from events where created_at::date = current_date - 1;

查询本身一直在运行。那是为什么?

2 个答案:

答案 0 :(得分:1)

尝试这个:

SELECT count(*) 
FROM events 
WHERE created_at >= current_date - 1
  AND created_at < current_date;

答案 1 :(得分:1)

因此,开始:为什么解释计划能够提供比查询运行更快的估计行数?

优化程序根据存储的统计信息和/或存储统计信息的推断来估计行数。如您所见,这不是非常准确的。 (根据评论讨论,估计值下降了近20%。)因此,查询必须根据表中的数据或索引中的数据进行实际计数。这是更多的工作。但是,为什么10分钟的“更多工作”并不明显。

一个合理的猜测是锁定争用。根据您的事务隔离设置,可能是您的查询必须等待对表的插入或更新才能完成。 (优化器在计算其估计时不会有这个问题,因为它只是假设并发查询的效果对于它的目的来说并不是什么大问题。)即使没有添加的数据会影响你的计数,表级也是如此锁可能仍然存在冲突。

测试这个理论的一种方法是复制表,这样你就可以得到一个没有人查询的相同数据(和相同的索引等)的表,看看你的计数是否运行得更快。

(顺便说一句:一般情况下,当统计数据显着偏离时,你可能会怀疑优化器选择了一个糟糕的执行计划;但是很难看出索引扫描在这里是如何做错的解决方案。)