我有下面的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倍。他们检索相似数量的记录,它们似乎都在使用索引。所以有人可以向我解释为什么这种性能差异吗?我可以做些什么来改善查询特定日期检索所有记录的查询的性能吗?
答案 0 :(得分:4)
我猜想这与数据布局有关。我猜您正在按stk
加载数据,因此给定stk
的行位于少数几页上,这些页面几乎只包含那个stk
。
因此,执行引擎仅读取约25页。
另一方面,没有一个页面包含同一日期的两个记录。按日期阅读时,您必须阅读约7,556页。也就是说,大约是页面数的300倍。
缩放也必须考虑加载和读取索引的工作。这两个查询应该大致相同,因此比率小于300。
答案 1 :(得分:2)
可能会有更多问题-因此很难说问题出在哪里。索引扫描通常应该比位图堆扫描更快-否则,可能存在以下问题:
REINDEX INDEX indexname
ANALYZE tablename
VACUUM tablename
effective_cache_size
设置得太高random_page_cost
调查什么是问题,这只是炼金术-但有可能-只有封闭的非常可能的问题集。好的开始是
VACUUM ANALYZE tablename
答案 2 :(得分:1)
要发现差异,您可能必须对查询运行EXPLAIN (ANALYZE, BUFFERS)
,以便查看触摸了多少块以及它们来自何处。
我可以想到两个原因:
错误的统计信息使PostgreSQL相信dt
具有很高的相关性,而没有。如果相关性较低,则位图索引扫描通常会更有效。
要查看是否存在问题,请运行
ANALYZE eods;
看看是否会改变所选的执行计划。
缓存效果:也许第一个查询找到了已经缓存的所有必需块,而第二个查询没有找到。
无论如何,值得尝试看看位图索引扫描对于第二个查询是否更便宜:
SET enable_indexscan = off;
然后重复查询。