Postgres 10升级后大查询出现DataFileRead类型IO等待问题求助
我来帮你捋一捋这个问题——升级到PG10后,涉及数千万行的关联查询出现wait_event_type: IO、wait_event: DataFileRead的等待,哪怕你已经对齐了大部分参数,也有几个容易被忽略的关键点:
统计信息未同步/不全
PG10的查询优化器对统计信息的依赖和PG9.6有细微差异,旧集群的统计信息是长期运行积累下来的,而新集群刚迁移完成,可能部分表的统计信息缺失或者预估不准。建议先对全库重新收集统计信息:ANALYZE VERBOSE;重点关注那些涉及数千万行的关联表,单独对它们执行
ANALYZE也可以。执行计划发生了变化
版本升级后,PG的优化器逻辑会有迭代,哪怕参数一致,同一个查询也可能生成不同的执行计划——比如从高效的索引扫描变成了全表扫描,或者连接方式从嵌套循环换成了哈希连接,这都会导致磁盘IO激增。你可以用EXPLAIN ANALYZE分别在新旧集群跑同一个查询,对比两者的执行计划:- 看扫描类型(Seq Scan还是Index Scan)
- 看连接方式(Nested Loop、Hash Join还是Merge Join)
- 看行数预估和实际返回行数的差距,如果差距很大,说明统计信息有问题
缓存未预热
旧集群长期运行,常用的数据早就被加载到shared_buffers和操作系统的页缓存里了,而新集群刚启动,缓存是空的。第一次执行大查询时,必然要从磁盘大量读取数据,自然会出现DataFileRead等待。你可以:- 手动预热缓存:对关联涉及的大表执行一次全表扫描(比如
SELECT * FROM large_table;,前提是内存能装下) - 重复执行几次查询,观察后续的等待情况是否缓解——如果第二次、第三次跑的时候IO等待减少,那就是缓存的问题
- 手动预热缓存:对关联涉及的大表执行一次全表扫描(比如
隐藏的参数差异
你提到除了几个参数外都一致,但PG10新增了一些参数,也调整了部分参数的默认值,比如work_mem、maintenance_work_mem的默认值在不同版本可能有变化,还有effective_io_concurrency这类和IO性能相关的参数。可以用SQL对比两个集群的所有参数:
在新集群执行(如果已经建立了到旧集群的dblink):SELECT s.name, s.setting AS new_setting, o.setting AS old_setting FROM pg_settings s JOIN dblink('dbname=old_db', 'SELECT name, setting FROM pg_settings') o(name text, setting text) ON s.name = o.name WHERE s.setting != o.setting;或者分别在两个集群导出参数列表,用文本对比工具找差异。
存储层面的差异
如果新集群的存储环境和旧集群不一样(比如磁盘类型从SSD换成了HDD,RAID级别变化,或者文件系统的挂载参数不同),也会导致IO性能下降。可以用iostat -x 1在查询执行时监控磁盘的使用率、读写速度,看看是不是磁盘本身的性能瓶颈。
先从统计信息和缓存预热这两个最容易解决的点入手,大概率能缓解问题,如果不行再逐步排查执行计划和参数差异。
内容的提问来源于stack exchange,提问作者onedrop

