内容提要
文章指出 PostgreSQL 的 recovery_prefetch 参数可能引发生产事故:它把主库的大顺序 I/O 转为备库的 8k 随机读,IOPS 放大最高 16 倍,易耗尽备库 IOPS 造成严重延迟;若同步复制连接中断,备库需追平 WAL 才恢复流复制,主库则阻塞所有写入,形成宕机。作者提供了复现方法,倾向全面禁用该参数,或采用双备库加 quorum commit 提升健壮性。
延伸解读
IOPS放大机制:从顺序I/O到随机读
文章指出,recovery_prefetch 的核心问题在于将主库的大顺序 I/O 转换为备库上的 8k 随机读。对于相同读取字节数,备库的 IOPS 可能被放大至 16 倍。这容易耗尽备库可用 IOPS,导致备库在写入活动后显著滞后。该机制与 FPI/FPW 无关,是独立于常见认知的新发现。
同步复制下的宕机链条
在同步复制中,主库提交依赖至少一个健康备库。当网络故障中断流复制,同时出现写入突发,备库因 IOPS 放大而严重滞后,并进入 WAL 恢复模式;该模式要求备库追平 WAL 后才恢复流复制。若追平耗时数小时,主库将阻塞所有写入,形成宕机。作者提供了可复现步骤。
禁用与架构缓解的权衡
作者倾向全面禁用 recovery_prefetch,测试显示禁用后(FPW 仍开启)备库不再出现读 IOPS 突发,能基本保持同步。另一种思路是采用双备库加 quorum commit(ANY 1),虽增加一个副本成本,但可提升同步复制的健壮性,避免单一备库连接中断即导致主库阻塞。
Q&A
PostgreSQL 的 recovery_prefetch 参数为什么会导致宕机?
recovery_prefetch 会将主库的大顺序 I/O 转换为备库的 8k 随机读,导致 IOPS 放大最高 16 倍,可能耗尽备库 IOPS,使备库严重延迟。在同步复制下,若流复制连接中断,备库需追平 WAL 才能恢复流复制,期间主库阻塞所有写入,从而引发宕机。
recovery_prefetch 对备库 IOPS 有什么具体影响?
对于相同的读取字节数,recovery_prefetch 会将顺序 I/O 转为 8k 随机读,使备库的 IOPS 放大最高 16 倍,容易耗尽备库可用 IOPS,导致备库在写入活动后显著滞后。
如何复现 recovery_prefetch 导致的宕机?
作者提供了复现步骤,详见 GitHub 仓库:https://github.com/ardentperf-labs/walreceiver-outage-repro。基本场景是:网络故障中断流复制的同时发生大量写入活动,备库因 IOPS 放大而严重延迟,无法及时恢复流复制,导致主库阻塞写入。
禁用 recovery_prefetch 能解决问题吗?效果如何?
禁用 recovery_prefetch(保持 FPW 启用)后,UPDATE 期间仍有暂停,但 UPDATE 完成后不再出现长时间暂停,没有读取 IOPS 的爆发,备库基本保持同步,应用 5% 的更新在 10 分钟内完成。作者倾向于全面禁用该参数。
除了禁用 recovery_prefetch,还有什么提升健壮性的方案?
可以考虑使用两个备库并配置 quorum commit(ANY 1),这样虽然总副本数变为三个、成本增加,但能显著提高数据库的健壮性,避免单备库故障导致主库阻塞。
为什么同步复制下备库延迟会导致主库阻塞写入?
在同步复制中,主库提交事务前必须确保 WAL 已传到至少一个健康备库。如果备库因 IOPS 放大而严重延迟,且流复制连接中断,备库需追平 WAL 才能重新进入流复制模式;在此期间主库无法接受写入,所有写操作被阻塞。