PostgreSQL ERROR: canceling statement due to conflict with recovery
PostgreSQL ERROR: canceling statement due to conflict with recovery
PostgreSQL 在主从模式下,当我们在从库上执行查询的时候常常会遇到这样的问题。
ERROR: canceling statement due to conflict with recovery
Detail: User query might have needed to see row versions that must be removed.
在一个一主一从的异步流复制架构模型中,在从库上执行一个长时间的查询,提示如下的错误信息:
ERROR: canceling statement due to conflict with recovery
Detail: User query might have needed to see row versions that must be removed.
原理分析:
位于主节点的相关表做了 DDL 或者 DML 操作,wal 日志在备节点回放的时候,位于备节点上的查询操作还在进行,如此就会发生冲突。当发生冲突的时候,取消了位于备节点上执行的查询语句,所以报出了以上的错误信息。翻译一下上面的错误信息:
错误:由于与恢复冲突而取消语句
详细信息:用户查询可能需要查看必须删除的行版本。
可以看一下官网资料:--> 26.5.2. 处理查询冲突
https://www.postgresql.org/docs/current/hot-standby.html#HOT-STANDBY-CONFLICT
强制性地取消与要被应用的 WAL 记录冲突的后备查询。
发生这种问题的原因主要分为两类:一类是 DDL、一类是 DML
对于 DDL 语句,可以设置参数 max_standby_streaming_delay 为 -1,表示会一直等待查询的语句结束为止。
对于 DML 语句,可以通过两种方式,一种是使用上面的方式,一种是设置参数 hot_standby_feedback 为 on。
hot_standby_feedback 的原理很简单,就是将备节点的最小活跃事务 ID 定期告知主节点,使得主节点在执行 VACUUM 时对这些事务还需要的数据不会删除。设置 hot_standby_feedback 的好处是可以减少备节点执行查询时复制冲突的可能,但也有其弊端,即会使主节点的相关表的数据延迟回收,从而导致数据膨胀;极端情况下,如果备节点有一个很长的事务,且涉及的表上 DML 操作又很频繁,则表的膨胀则会很严重。
此外还可以考虑设置 vacuum_defer_cleanup_age 参数的值,这个参数可以延迟清理 dead row,使得在备节点的查询可以读取这些 dead row,该参数的默认为 0,这个参数是在主节点上设置的。但是这种方法很难保证任何指定的执行时间窗口,因为 vacuum_defer_cleanup_age 是用主服务器上被执行的事务数来衡量的。查询取消的数量和原因可以使用后备服务器上的系统视图 pg_stat_database_conflicts 来查看。
在主库上执行:
SELECT * FROM pg_stat_replication;
sync_state 字段的值为 async 表示架构模型是异步的
SHOW max_standby_streaming_delay;
默认是 30S
参数 max_standby_streaming_delay 解释:当热后备机处于活动状态时,这个参数决定取消那些与即将应用的 WAL 项冲突的后备机查询之前,后备服务器应该等待多久。当 WAL 数据正在通过流复制被接收时,max_standby_streaming_delay 可以应用。默认值是 30 秒。如果没有指定,衡量单位是毫秒。值 -1 允许后备机一直等到冲突查询结束。这个参数只能在 postgresql.conf 文件中或在服务器命令行上设置。
注意:max_standby_streaming_delay 与取消一个查询能够运行的最长时间不同;它表示从主服务器接收到 WAL 数据并立刻应用它能够被允许的最长总时间。
允许一个 SQL 指令执行的最长时间是由参数 statement_timeout 来决定的。
在从库上执行:
SHOW hot_standby_feedback;
默认是关闭的 off
参数 hot_standby_feedback 指定一个热后备机是否将会向主服务器或上游后备机发送有关于后备机上当前正被执行的查询的反馈。这个参数可以被用来排除由于记录清除导致的查询取消,但是可能导致在主服务器上用于某些负载的数据库膨胀。反馈消息的发送频度不会高于每个 wal_receiver_status_interval 周期发送一次。默认值是 off。这个参数只能在 postgresql.conf 文件中或在服务器命令行上设置。如果使用级联复制,反馈将被向上游传递直到它最后到达主服务器。后备机在接收到反馈之后除了传递给上游不会做任何其他操作。这个设置不会覆盖主服务器上的 old_snapshot_threshold 的行为,后备服务器上一个超过了主服务器年龄阈值的快照可能会变得不可用,导致后备服务器上事务的取消。这是因为 old_snapshot_threshold 是为了对死亡行能够存在的时间给出一个绝对限制,不然就会因为一个后备服务器的配置而被违背。
vacuum_defer_cleanup_age (integer)
Specifies the number of transactions by which VACUUM and HOT updates will defer cleanup of dead row versions. The default is zero transactions, meaning that dead row versions can be removed as soon as possible, that is, as soon as they are no longer visible to any open transaction. You may wish to set this to a non-zero value on a primary server that is supporting hot standby servers, as described in Section 26.5. This allows more time for queries on the standby to complete without incurring conflicts due to early cleanup of rows. However, since the value is measured in terms of number of write transactions occurring on the primary server, it is difficult to predict just how much additional grace time will be made available to standby queries. This parameter can only be set in the postgresql.conf file or on the server command line.
You should also consider setting hot_standby_feedback on standby server(s) as an alternative to using this parameter.
This does not prevent cleanup of dead rows which have reached the age specified by old_snapshot_threshold.
查看视图:
SELECT * FROM pg_stat_database_conflicts;