hot_standby=on仅允许只读查询,写操作必报错;hot_standby_feedback=on可缓解查询冲突但导致主库膨胀和从库延迟升高;冲突定位需结合日志与系统视图分析。

hot_standby 设置为 on 后仍报 “cannot execute INSERT in a read-only transaction”
这是最典型的误判:以为开了 hot_standby = on 就能随便读写。其实它只允许只读查询,所有写操作(INSERT、UPDATE、DELETE、CREATE)在从库上直接报错,不是延迟问题,是设计限制。
常见错误现象:ERROR: cannot execute INSERT in a read-only transaction;哪怕从库已同步完成、pg_stat_replication 显示 state = streaming,也一样报错。
- 确认连接的确实是备库(查
SELECT pg_is_in_recovery();,返回t才是) - 写操作必须路由到主库,不能靠“等同步完再写从库”来绕过——PostgreSQL 不支持这种模式
- 应用层需明确区分读/写连接池,或用中间件(如 PgBouncer + 规则路由)隔离
hot_standby_feedback = on 导致主库 vacuum 停滞和 bloat
hot_standby_feedback = on 的作用是让从库告诉主库:“我还在用这些数据页”,从而阻止主库 VACUUM 回收某些死元组。这能缓解查询冲突(query canceled due to conflict with recovery),但代价是主库表膨胀风险上升。
使用场景:仅当从库有长事务(> 数分钟)、且频繁执行大范围 SELECT(如报表导出、ETL 拉取)时才考虑开启;普通 OLAP 查询或短查询完全不需要。
- 开启前先观察主库
pg_stat_all_tables.n_dead_tup和pg_total_relation_size()增长趋势 - 必须配合
max_standby_streaming_delay(默认 30s)使用,否则从库卡住会导致主库长期不敢清理 - 若从库偶尔卡顿,建议设为
max_standby_streaming_delay = 30s而非-1(无限等待)
查询冲突报错 “query canceled due to conflict with recovery” 怎么定位源头
这个错误不是随机出现的,一定对应某个正在被主库 VACUUM 或 CLUSTER 清理的数据页,而从库恰好要读它。关键不是“怎么关掉冲突”,而是“谁在触发它”。
错误信息本身不带具体表名,需结合日志和系统视图交叉定位:
- 在从库开启
log_min_messages = debug2+log_line_prefix = '%m [%p] %q%u@%d ',复现时抓完整日志行,里面会含conflict type: Snapshot或BufferPin - 查主库
SELECT pid, usename, query FROM pg_stat_activity WHERE state = 'active' AND query ~ 'VACUUM|CLUSTER'; - 检查从库慢查询:是否有未加
LIMIT的全表扫描、或ORDER BY ... OFFSET导致长时间持有快照
延迟高时开启 hot_standby_feedback 反而让延迟更高
开启 hot_standby_feedback = on 后,主库会暂缓清理,导致 WAL 日志中需重放的变更堆积更多(尤其大事务后),从库 replay 压力增大,延迟反而拉长。这不是 bug,是反馈机制的副作用。
性能影响明显出现在两类场景:主库高频更新小表(如计数器表)、或从库配置较低(CPU/IO 跟不上 replay 速度)。
- 监控指标重点看从库
pg_stat_replication.replay_lag和主库pg_stat_bgwriter.buffers_checkpoint是否突增 - 不要全局开启:可在特定从库(如专用报表节点)单独设
ALTER SYSTEM SET hot_standby_feedback = on,其他只读节点保持 off - 更稳妥的替代方案是调低主库
vacuum_defer_cleanup_age(默认 0),让VACUUM更早清理,减少冲突概率
真正难处理的不是开关本身,而是“从库查询到底依赖哪条快照”——它藏在事务启动瞬间,没法 runtime 修改。所以冲突排查永远要从查询生命周期入手,而不是盯着参数调来调去。










