更新时间:2024-09-06 GMT+08:00
分享

只读查询复制冲突

什么是流复制冲突?

PostgreSQL的主库与只读库之间使用流复制进行数据同步,由于主库会不断产生wal日志,当这些wal日志同步到只读库,进行日志应用(Apply)过程中会与只读库的查询(Query)产生冲突。

简单来说,就是只读库查询(Query)与Wal日志应用(Apply)冲突。当只读库的恢复进程无法应用从主库同步过来的wal时,发生流复制冲突。

当发生流复制冲突时,我们会在只读库中看到以下错误日志:

ERROR: canceling statement due to conflict with recovery

为什么会产生流复制冲突?

当只读库正在执行某个表的查询(可能是业务应用产生,也可能是手动连接执行查询),这时主库执行了DROP TABLE操作,在该操作写入wal日志并同步到只读库后,为了保证数据一致性,只读库会迅速应用DROP TABLE操作,这时DROP TABLESELECT就会形成冲突。以下几种场景会产生流复制冲突:

  1. 只读库中对某个表正在进行查询(ACCESS SHARE),需要应用wal日志中的ACCESS EXCLUSIVE锁的操作。例如:DROP TABLETRUNCATE TABLE、大多数ALTER TABLE等操作。
  2. 主库vacuum清理死元组造成的冲突。若只读在主库执行vacuum之前就启动了一个查询,主库vacuum处理掉了只读库所需要的死元组时,就会产生冲突。

发生流复制冲突时怎么办?

参数控制

PostgreSQL提供了以下参数用于控制流复制冲突:

  • max_standby_streaming_delay

    参数说明:参数默认为30秒,表示当只读库执行SQL时,有可能与正在应用的wal发生冲突,此查询如果30秒没有执行完成则被中止。

    设置为“-1”表示遇到冲突时,直到只读库完成查询后,再继续进行wal回放。注意,“max_standby_streaming_delay”与取消之前一个查询能够运行的最长时间不同,它表示在从主库接收到 wal数据并立刻应用它能够被允许的最长总时间。因此,如果一个查询导致了明显的延迟,后续冲突查询只有更少的时间,直到只读库再次赶上进度。

    缺点:该参数设置时间过长,或设置为“-1”时,若只读存在长事务,则会导致主库与只读库之间存在一定的数据时延。

  • hot_standby_feedback

    参数说明:设置为“on”后只读库执行查询时会通知主库,在只读库执行查询过程中,主库不会清理只读库需要的数据,因此也不会发生因vacuum导致的流复制冲突。

    缺点:设置为“on”时,可以解决因vacuum导致的流复制冲突,DROP等操作导致的冲突依然存在。同时若只读中存在长事务,会导致数据库中死元组不能及时清理,造成数据库膨胀。

优化建议

  1. 优化只读实例的SQL,将SQL查询时长控制在“max_standby_streaming_delay”值以内。
  2. 监控只读长事务,根据业务需求,超过一定时长(如30min)的长事务要强制终止。

相关文档