在PostgreSQL中,我在tickets表的日期字段上有一个索引。当我将字段与进行比较时now(),查询效率很高:
tickets
now()
# explain analyze select count(1) as count from tickets where updated_at > now(); QUERY PLAN --------------------------------------------------------------------------------------------------------------------------------------------- Aggregate (cost=90.64..90.66 rows=1 width=0) (actual time=33.238..33.238 rows=1 loops=1) -> Index Scan using tickets_updated_at_idx on tickets (cost=0.01..90.27 rows=74 width=0) (actual time=0.016..29.318 rows=40250 loops=1) Index Cond: (updated_at > now()) Total runtime: 33.271 ms
如果我尝试将其与now()负间隔进行比较,它会下坡并使用位图堆扫描。
# explain analyze select count(1) as count from tickets where updated_at > (now() - '24 hours'::interval); QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------------------- Aggregate (cost=180450.15..180450.17 rows=1 width=0) (actual time=543.898..543.898 rows=1 loops=1) -> Bitmap Heap Scan on tickets (cost=21296.43..175963.31 rows=897368 width=0) (actual time=251.700..457.916 rows=924373 loops=1) Recheck Cond: (updated_at > (now() - '24:00:00'::interval)) -> Bitmap Index Scan on tickets_updated_at_idx (cost=0.00..20847.74 rows=897368 width=0) (actual time=238.799..238.799 rows=924699 loops=1) Index Cond: (updated_at > (now() - '24:00:00'::interval)) Total runtime: 543.952 ms
有没有一种更有效的使用日期算术查询的方法?
第一个查询希望找到 rows=74 ,但实际上找到rows=40250。 第二个查询期望找到 rows=897368 并实际找到rows=924699。
rows=74
rows=40250
rows=897368
rows=924699
当然,处理23倍的行会花费更多的时间。因此,您的实际时间不足为奇。
的数据统计信息updated_at > now()已过时。跑:
updated_at > now()
ANALYZE tickets;
并重复您的查询。而您是否认真地拥有数据updated_at > now()呢?听起来不对。
但是,对于最近更改的数据而言,统计数据已过时也就不足为奇了。这是合乎逻辑的。如果查询依赖于当前统计信息,则必须先运行,ANALYZE然后再运行查询。
ANALYZE
还要进行测试(仅在您的会话中):
SET enable_bitmapscan = off;
并重复第二次查询以查看没有位图索引扫描的时间。
普通 索引扫描 按索引中的顺序从堆中获取行。这很简单,愚蠢而且没有开销。几行速度很快,但与行数不断增加的位图索引扫描相比,最终成本可能更高。
一个 位图索引扫描 从索引中收集行之前查表。如果同一数据页上有多个行,则可以节省重复访问的时间,并且可以使处理速度大大提高。行越多,机会越大,位图索引扫描将节省时间。
对于更多的行(大约占表的5%,很大程度上取决于实际数据),计划器将切换到表的 顺序扫描 ,并且根本不使用索引。
最佳的选择是Postgres 9.2引入的 仅索引扫描 。只有满足一些前提条件才有可能。如果所有相关列都包含在索引中,则索引类型支持该索引,并且可见性映射表指示数据页上的所有行对所有事务可见,而不必从堆(表)中获取该页,并且索引中的信息就足够了。
该决定取决于您的统计信息(Postgres希望找到多少行及其分布)以及成本设置,最重要的是random_page_cost,cpu_index_tuple_cost和effective_cache_size。
random_page_cost
cpu_index_tuple_cost
effective_cache_size