查询PostgreSQL表的效率问题

时间:2019-06-23 03:20:15

标签: sql postgresql

我有下面的PostgreSQL表,其中有大约6700万行,该表存储了从1985年开始的所有美国股票的EOD价格:

not recognized
hi

我想根据股票名称或日期有效地查询上表。该表的主键是股票名称和日期。我还在日期列上定义了一个索引,以期提高检索特定日期所有记录的查询的性能。

不幸的是,我发现以下查询在性能上有很大差异。获取特定股票的所有记录需要花费相当长的时间(2秒),而获取特定日期的所有记录则需要更长的时间(约56秒)。我尝试使用 Table "public.eods" Column | Type | Collation | Nullable | Default --------+-----------------------+-----------+----------+--------- stk | character varying(16) | | not null | dt | date | | not null | o | integer | | not null | hi | integer | | not null | lo | integer | | not null | c | integer | | not null | v | integer | | | Indexes: "eods_pkey" PRIMARY KEY, btree (stk, dt) "eods_dt_idx" btree (dt) 分析这些查询,并得到以下结果:

explain analyze
explain analyze select * from eods where stk='MSFT';
                                                          QUERY PLAN                                                          
------------------------------------------------------------------------------------------------------------------------------
 Bitmap Heap Scan on eods  (cost=169.53..17899.61 rows=4770 width=36) (actual time=207.218..2142.215 rows=8364 loops=1)
   Recheck Cond: ((stk)::text = 'MSFT'::text)
   Heap Blocks: exact=367
   ->  Bitmap Index Scan on eods_pkey  (cost=0.00..168.34 rows=4770 width=0) (actual time=187.844..187.844 rows=8364 loops=1)
         Index Cond: ((stk)::text = 'MSFT'::text)
 Planning Time: 577.906 ms
 Execution Time: 2143.101 ms
(7 rows)

我真的不明白为什么第二个查询的运行速度比第一个查询慢28倍。他们检索相似数量的记录,它们似乎都在使用索引。所以有人可以向我解释为什么这种性能差异吗?我可以做些什么来改善查询特定日期检索所有记录的查询的性能吗?

3 个答案:

答案 0 :(得分:4)

我猜想这与数据布局有关。我猜您正在按stk加载数据,因此给定stk的行位于少数几页上,这些页面几乎只包含那个stk

因此,执行引擎仅读取约25页。

另一方面,没有一个页面包含同一日期的两个记录。按日期阅读时,您必须阅读约7,556页。也就是说,大约是页面数的300倍。

缩放也必须考虑加载和读取索引的工作。这两个查询应该大致相同,因此比率小于300。

答案 1 :(得分:2)

可能会有更多问题-因此很难说问题出在哪里。索引扫描通常应该比位图堆扫描更快-否则,可能存在以下问题:

  • 不健康的索引-尝试运行REINDEX INDEX indexname
  • 错误的统计信息-尝试运行ANALYZE tablename
  • 表的
  • 次优状态-尝试运行VACUUM tablename
  • 设置得太低或effective_cache_size设置得太高
  • 带有IO的问题-某些系统存在随机IO较高的问题,请尝试增加random_page_cost

调查什么是问题,这只是炼金术-但有可能-只有封闭的非常可能的问题集。好的开始是

  • VACUUM ANALYZE tablename
  • 对您的IO进行基准测试(例如bonie ++)

答案 2 :(得分:1)

要发现差异,您可能必须对查询运行EXPLAIN (ANALYZE, BUFFERS),以便查看触摸了多少块以及它们来自何处。

我可以想到两个原因:

  1. 错误的统计信息使PostgreSQL相信dt具有很高的相关性,而没有。如果相关性较低,则位图索引扫描通常会更有效。

    要查看是否存在问题,请运行

    ANALYZE eods;
    

    看看是否会改变所选的执行计划。

  2. 缓存效果:也许第一个查询找到了已经缓存的所有必需块,而第二个查询没有找到。

无论如何,值得尝试看看位图索引扫描对于第二个查询是否更便宜:

SET enable_indexscan = off;

然后重复查询。