【问题标题】:Efficient PostgreSQL query on timestamp using index or bitmap index scan?使用索引或位图索引扫描对时间戳进行高效 PostgreSQL 查询?
【发布时间】:2014-09-05 16:42:03
【问题描述】:

在 PostgreSQL 中,我的 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

有没有更有效的使用日期算法进行查询的方法?

【问题讨论】:

  • 版本? select version()。发布表架构\d tickets
  • 你有ANALYZE桌子吗?顺便说一句,第一个查询使用仅索引扫描,而第二个查询处理更多记录并且必须扫描表本身。
  • @IgorRomanchenko:我在第一个查询中看到了索引扫描,而不是仅索引扫描。
  • 40250 行或 924699 行,有区别...
  • 这不是添加本身。将 now()now() - '24 hours'::interval 替换为各自的时间戳文字,您会得到相同的结果。重要的是要找到的 预期 行数(74 对 897368)。正如我的回答中所解释的那样。

标签: sql postgresql indexing sql-execution-plan postgresql-performance


【解决方案1】:

第一个查询期望找到 rows=74,但实际上找到了 rows=40250
第二个查询期望找到 rows=897368,实际上找到了rows=924699

当然,处理 23 x 多的行需要相当多的时间。所以你的实际时间并不奇怪。

updated_at > now() 数据的统计信息已过时。运行:

ANALYZE tickets;

并重复您的查询。你真的有updated_at > now() 的数据吗?听起来不对。

不过,对于最近更改的数据,统计信息已过时也就不足为奇了。这是事物的逻辑。如果您的查询依赖于当前统计信息,您必须在运行查询之前运行ANALYZE

同时测试(仅在您的会话中):

SET enable_bitmapscan = off;

并重复您的第二个查询以查看没有位图索引扫描的时间。

为什么位图索引扫描更多行?

普通的索引扫描会按照在索引中找到的顺序从堆中获取行。这很简单,愚蠢而且没有开销。几行快速,但最终可能比行数不断增加的位图索引扫描更昂贵。

位图索引扫描在查找表之前从索引中收集行。如果多行驻留在同一数据页面上,则可以节省重复访问,并且可以使事情变得更快。行数越多,位图索引扫描的机会就越大。

对于更多行(大约 5% 的表,很大程度上取决于实际数据),规划器切换到表的顺序扫描,根本不使用索引。

最佳选择是 index only scan,它是 Postgres 9.2 引入的。这只有在满足一些先决条件的情况下才有可能。如果索引中包含所有相关列,则索引类型支持它,并且可见性映射表明数据页上的所有行对所有事务都是可见的,则不必从堆(表)中获取该页,并且索引中的信息就足够了。

决定取决于您的统计数据(Postgres 期望找到多少行及其分布)和cost settings,最重要的是random_page_costcpu_index_tuple_costeffective_cache_size

【讨论】:

  • 所以听起来我应该升级到 9.3 看看问题是否仍然存在。无论如何我都应该升级。
  • 哦,我觉得很傻。我了解 updated_at > NOW() - 当我玩查询时,我什至没有考虑过这一点。当然行数更少。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-02-14
  • 2020-09-06
  • 2012-05-23
  • 2015-03-18
  • 2017-06-24
相关资源
最近更新 更多