为何已创建覆盖索引仍出现堆获取(Heap Fetch)?
看起来你在PostgreSQL里遇到了覆盖索引没按预期工作的问题——插入2000万行数据后建了覆盖索引,但还是出现了堆获取,我来帮你梳理下几个最可能的原因:
1. 可见性映射(Visibility Map)没跟上
你一次性插入了巨量数据,之后如果没做过VACUUM操作,PostgreSQL的**可见性映射(VM)**可能还没标记这些新插入的元组为“全可见”。即使你的覆盖索引已经包含了查询需要的所有字段,数据库还是得去堆里验证元组的可见性,这就产生了堆获取。
解决方法很直接,执行一次:
VACUUM ANALYZE students;
这个操作会更新可见性映射,同时刷新表的统计信息,一举两得。
2. 查询条件的选择性太差
如果你的查询条件匹配的数据量太大(比如WHERE g < 50,而g的范围是0-100,一半数据都符合),PostgreSQL的优化器可能会觉得:“走索引再去堆里验证(甚至直接全表扫描)比纯索引扫描更划算”,这时候就会放弃索引-only扫描,转而使用普通索引扫描或者全表扫描,自然会有堆获取。
你可以用EXPLAIN ANALYZE看看执行计划,比如:
EXPLAIN ANALYZE SELECT id FROM students WHERE g = 42;
如果结果里是Index Scan而不是Index Only Scan,那大概率是这个原因。
3. 查询用到了索引没覆盖的字段
再仔细检查下你的查询语句,如果除了id和g之外,还请求了其他字段(比如firstname、address),那即使你的索引包含了id,也得去堆里获取那些没在索引里的字段,堆获取肯定会出现。
4. 统计信息过时
插入2000万行数据后,PostgreSQL的统计信息可能还是旧的,优化器没办法准确判断数据分布,从而选错了执行计划。执行ANALYZE students;可以更新统计信息,让优化器做出更合理的选择。
总结一下,先跑VACUUM ANALYZE,再用EXPLAIN ANALYZE看执行计划,基本就能定位问题啦。
备注:内容来源于stack exchange,提问作者Sathish

