备用库查询报 shared buffer pin,hot_standby_feedback 为什么没挡住
物理备用库开启反馈后,长查询仍可能因 buffer pin 恢复冲突被取消。结合日志、冲突计数和 WAL 等待预算调整报表任务与复制延迟。
物理备用库上的长查询被取消,日志出现:
ERROR: canceling statement due to conflict with recovery
DETAIL: User was holding shared buffer pin for too long
即使 hot_standby_feedback 已开启,这类冲突仍可能发生。先按 DETAIL 判断冲突类型,不要只根据第一行把所有恢复冲突归到同一个参数上。
feedback 不能消除 buffer pin 冲突
备用库一边接受只读查询,一边回放主库传来的 WAL。查询访问某个共享缓冲区时可能持有 pin;恢复进程执行某些页面清理操作需要相应的清理锁。如果两者不能同时推进,备用库需要选择等待回放或取消查询。
hot_standby_feedback 向主库反馈备用库查询所需的事务信息,减少主库清理旧行版本带来的冲突。它不会让已经收到的 WAL 清理记录跳过回放,也不保证所有 buffer pin 冲突消失。开启反馈还可能推迟主库清理、增加表膨胀,应一起观察。
这套区别适用于物理热备用,不是逻辑复制订阅的 worker 名额问题。先在发生错误的连接目标确认身份与设置:
SELECT version(), pg_is_in_recovery();
SELECT name, setting, unit, context, pending_restart
FROM pg_settings
WHERE name IN (
'hot_standby_feedback',
'max_standby_streaming_delay',
'max_standby_archive_delay'
)
ORDER BY name;
pg_is_in_recovery() 应与预定备用库角色一致。检查实际生效值,不仅查看配置文件;两项延迟在主库上没有这个作用。
30 秒不是每条查询都能获得的运行时间
max_standby_streaming_delay 控制回放流复制收到的 WAL 时允许等待的时间,max_standby_archive_delay 对应从归档读取 WAL。应按当前 WAL 来源判断,不能只改名字里有 streaming 的一项。
这两项限制回放已经收到的 WAL 可以等待多久,并非每条查询各自拥有的 statement_timeout。前面的查询或繁忙负载已消耗等待预算时,新遇到的冲突查询可能很快被取消;一条长查询没有碰到冲突时,也可能正常完成。
因此,同一条 SQL 晚间能跑完、早晨由 cron 执行失败,不能单凭这两次结果认定 cron 导致错误。需要对齐连接实例、角色、SQL、执行计划和回放负载,再判断差异。
用冲突计数和日志定位这一轮
在备用库读取当前数据库的冲突统计:
SELECT datname, confl_bufferpin, confl_snapshot, confl_lock
FROM pg_stat_database_conflicts
WHERE datname = current_database();
这些是累计值,不是一条失败查询的归因报告。记录任务前后读数,结合相同时间段的服务器日志,确认新增的是 confl_bufferpin、快照冲突还是锁冲突;读数不增加时,还要核对连接目标、统计更新时间和是否重置。
同时查看现有复制监控中的接收、回放位置和积压。回放暂停、资源不足与查询阻挡可能有不同处理办法;仅看到查询运行了 30 秒,无法确认备用库此前已经落后多久。
调整报表任务与回放等待的取舍
允许重新执行的纯读取报表,可以在失败事务结束后做有次数和总时长限制的重试。恢复冲突取决于当时回放的 WAL,下一次可能成功,但不应把无限重试当作处理办法。任务若还包含导出文件、发送结果或其他副作用,要避免重试重复执行这些步骤。
对频繁取消的查询,检查执行计划、读取范围与运行时段,评估分批读取或移到承担报表职责的副本。分批会改变多次查询的一致性范围,需要按报表要求决定,不能仅为避开取消就拆分。
如果允许备用库暂时落后,可在复制延迟预算内提高对应的 max_standby_*_delay。由管理员按实际版本、配置管理方式和参数生效要求调整,保留原值;先限定观察窗口,同时核对回放积压和主库清理情况。延迟超过业务上限时恢复原设置,并调整报表的执行位置或方式。
承担故障切换的备用库通常不能为了单个报表无限等待。修正的判断依据应包括报表能否在预定窗口完成、同类冲突的新增数量,以及副本回放延迟是否仍符合业务要求。


