我有一个包含16列的表,其中有一个主键和一个用于存储值的列。 我想选择某个范围内的所有值。 值列(easyid)已被编入索引。
create table tb1 (
id Int primary key,
easyid Int,
.....
)
create index i_easyid on tb1 (easyid)
其他信息:postgresql 9.4,没有自动真空。 sql就是这样的。
select "easyid" from "tb1" where "easyid" between 12183318 and 82283318
理论上postgresql应该在i_easyid
上使用仅索引扫描。 仅在范围 "easyid" between A and B
较小时才进行索引扫描。
当范围很大,即B-A
是一个相当大的数字时,postgresql在i_easyid
上使用位图索引扫描,然后在tb1
上进行位堆扫描。
我错误地说索引扫描与否取决于范围大小。 我尝试了不同参数的相同查询,有时它只是索引扫描,有时它不是。
表格tb1
非常大,最高可达17G。 i_easyid
是600MB。
这是sql的解释。我不明白为什么4000行的成本可能超过10秒。
sample_pg=# explain analyze select easyid from tb1 where "easyid" between 152183318 and 152283318;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on tb1 (cost=97.70..17227.71 rows=4416 width=4) (actual time=1.155..14346.311 rows=5004 loops=1)
Recheck Cond: ((easyid >= 152183318) AND (easyid <= 152283318))
Heap Blocks: exact=4995
-> Bitmap Index Scan on i_easyid (cost=0.00..96.60 rows=4416 width=0) (actual time=0.586..0.586 rows=5004 loops=1)
Index Cond: ((easyid >= 152183318) AND (easyid <= 152283318))
Planning time: 0.080 ms
Execution time: 14348.037 ms
(7 rows)
以下是仅索引扫描的示例:
sample_pg=# explain analyze verbose select easyid from tb1 where "easyid" between 32280318 and 32283318;
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------------
Index Only Scan using i_easyid on public.tb1 (cost=0.44..281.82 rows=69 width=4) (actual time=14.585..160.624 rows=33 loops=1)
Output: easyid
Index Cond: ((tb1.easyid >= 32280318) AND (tb1.easyid <= 32283318))
Heap Fetches: 33
Planning time: 0.085 ms
Execution time: 160.654 ms
(6 rows)
答案 0 :(得分:9)
autovacuum未运行
PostgreSQL仅索引扫描需要一些关于哪些行对当前事务“可见”的信息 - 即未删除,而不是旧版本的更新行,而不是未提交的插入或新版本的更新。
此信息保存在“可见性图”中。
可见性图由VACUUM
维护,通常由autovacuum工作人员在后台维护。
如果autovacuum没有很好地跟上写入活动,或者如果autovacuum已被禁用,则可能不会使用仅索引扫描,因为PostgreSQL会看到可见性映射没有足够的表的数据。
重新打开autovaccum。然后手动VACUUM
表格,立即将其更新。
VACUUM
还可以写入提示位信息,使得最近插入/更新的数据的SELECT
更快。
Autovacuum还维护对有效查询计划至关重要的表统计信息。关闭它将导致计划者使用越来越陈旧的信息。
它也是绝对重要的,用于防止称为事务ID环绕的问题,这是一种紧急情况,可能导致整个数据库进入紧急关闭状态,直到耗费时间执行整个表VACUUM
。
不要关闭autovacuum 。
至于为什么它有时使用仅索引扫描而有时不使用,有几种可能性:
当前random_page_cost
设置使其认为随机I / O会比实际速度慢,所以它会更加努力地避免它;
表统计信息,特别是限制值已过时。因此,它没有意识到在一个仅索引的扫描中很快就会发现所寻找的值;
可见性图已过时,因此它认为仅索引扫描会找到太多值,需要检查堆读取,使其比其他方法慢,特别是如果它认为值的比例可能是发现很高。
大多数问题都是由单独保留autovacuum 修复的。实际上,在频繁附加的表中,您应该将autovacuum设置为比默认值更频繁地运行 ,以便更新更新限制统计信息。 (这样做有助于解决PostgreSQL的计划程序问题,其中最常查询的数据是最近插入的,具有递增ID或时间戳,这意味着最期望的值永远不会出现在表格直方图和限制统计数据中)。
重新启用autovacuum - 然后将其打开。
答案 1 :(得分:2)
我不是百分百肯定,但我怀疑PostgreSQL认为读取表比索引更快,因为random_page_cost。索引读取的成本可能更高,因为需要在其中找到基本上随机的页面。
从表中检索的数据将需要排序,但计算可能表明(顺序表读取+排序)的总成本大于(随机索引读取)。
通过更改random_page_cost的值可以部分测试,如果您使用的是非常快的磁盘或SSD,那么值得研究。