PostgreSQL高并发写入负载下查询不使用索引的原因咨询
下面针对你遇到的select * from book where obj_id=465789这类查询不使用索引的场景,拆解高并发写入的影响逻辑:
统计信息失效误导优化器
高并发写入会在短时间内剧烈改变表的数据分布——比如批量插入同obj_id的行,或者大量删除某类数据。PostgreSQL的自动统计信息更新默认是基于数据变更比例触发的,当变更速度远超统计更新速度时,优化器拿到的还是旧的统计数据:比如它以为obj_id=465789只有少数几行,索引扫描代价低,但实际该值对应几万行,全表扫描反而更快,最终选错执行计划。索引膨胀与锁推高扫描代价
频繁的插入、更新、删除会导致索引页面频繁分裂,产生大量空的或半满的索引页面(也就是索引膨胀),这会让索引扫描需要读取更多的磁盘页,IO成本直接飙升,甚至超过全表扫描。同时,写入操作会持有索引的各类锁,查询请求要获取索引锁时可能遭遇等待,优化器会预判到这种等待的时间成本,转而选择不需要争抢索引锁的全表扫描。MVCC可见性判断增加额外开销
PostgreSQL的MVCC机制下,索引扫描需要回表验证行的可见性——判断该行是否被当前事务可见、是否是已删除未清理的死元组。高并发写入时,活跃事务多,死元组堆积快,索引扫描的每一步都要额外做可见性判断,这会让索引扫描的实际执行时间大幅增加。当这种额外开销超过全表扫描的成本时,优化器就会放弃索引。执行计划缓存的滞后性
如果在写入负载较低时,优化器生成了全表扫描的执行计划并缓存,后续即使数据分布变化、统计信息更新,可能因为计划缓存的存在,查询仍会沿用旧计划。这种情况可以通过EXPLAIN ANALYZE查看当前执行计划,或者手动执行ANALYZE book刷新统计信息、强制重新生成计划。
内容的提问来源于stack exchange,提问作者Pioneer64

