PostgreSQL为什么要设置hot_standby_feedback?

2019-12-27 20:16:25 浏览数 (1)

问题背景

Postgresql从9.1开始支持流复制,流复制的出现是一次革命,因为它速度非常快,性能很好。流复制是基于wal日志的复制技术,主库不断发送wal日志至备库,备库进行应用回放。

但是有时我们可能会在备库进行某个查询,然后遇到查询中途突然抛出如下错误:

代码语言:javascript复制
ERROR:canceling statement due to confilct with recovery。

遇到这个错误很不幸,因为我们并不希望在备库运行一个长查询的最后时刻被告知查询取消。那么这个问题的原因是什么?又应该怎样预防呢?下面我们来探讨一下。

从报错我们可以看出,查询取消的原因是因为和恢复进程发生了冲突。那么为什么会产生冲突呢?我们细想一下,比如说备库正在执行基于某个表的查询(这个查询可能是应用产生的,也可能是手动连接进行的查询),这时主库执行了drop table操作,该操作写入wal日志后传至备库进行应用,为了保证数据一致性,postgresql必然会迅速回放数据,这时drop table和select就会形成冲突。如下图所示:

问题原因

这样我们就知道了可能造成冲突的场景,总结一下有以下几种情况:

①主库由于产生请求排他锁的操作传递至备库产生的冲突,例如上图所示,主库执行drop table、truncate table、alter table等操作,在备库进行回放时都有可能与备库正在进行的查询冲突。

②由于主库vacuum清理掉无用元组造成的冲突,当某些由于频繁更新或删除的表中vacuum进程发现某个页面中全部都是dead tuple(死亡元组)时,会尝试请求排他锁来进行清理,这样的话可能会与备库的查询产生冲突。那么普通vacuum会造成冲突吗?答案是肯定的。比如备库进行一个查询没有结束,如果主库vacuum掉了备库查询所需要的元组时,就会产生冲突。

问题预防

其实还有一种情况会造成查询冲突,说这种情况之前我们先来看看如何进行查询冲突的预防。下面的图片指明了查询冲突产生的流程:

我们可以通过设置如下几个参数进行查询冲突的控制。

hot_standby_feedback:

这个参数是查询冲突这个话题中提到最多的参数,下面我们详细探讨一下。我们假设在没有备库的情况下,会话1查询某行数据,会话2删除该数据,然后commit,此时会话2执行一次vacuum,我们知道这次vacuum并不会删除该行数据,因为会话1的事务还需要使用该元组,所以不会清理该元组。那么如果是主从呢?主库在准备进行vacuum时怎么知道从库还在进行查询,这就是设置该参数的意义,设置hot_standby_feedback参数之后备库会定期向主库通知最小活跃事务id(xmin)值,这样使得主库vacuum进程不会清理大于xmin值的事务。

这个参数有利于减少冲突的发生,但并不能完全避免冲突,其实细想一下,这个参数只是减少了由于主库vacuum死亡元组造成的冲突,并不能解决排他锁造成的冲突。同时还有我们上面提到的第三种情况:由于网络中断造成的冲突,假如主备之间的网络中断后,备库就无法向主库正常发送xmin值,如果时间够长,主库在这段时间内还是会清理无用元组,这样网络恢复后就可能发生上面的vacuum造成的冲突。

这个参数的设置是有利有弊,好处就是减少了冲突,缺点就是由于主库的清理需要等待备库的事务结束,那么在频繁更新的场景下,可能造成主库数据膨胀。所以我们在生产中设置hot_standby_feedback一般与下面几个参数一起使用,能够有效的降低冲突发生的概率。

值得注意的是hot_standby_feedback参数并不会覆盖主库上old_snapshot_threshold参数限定的值,old_snapshot_threshold参数限制了死亡元组的无限膨胀,当事务信息超过old_snapshot_threshold的限制时,依然会进行清理。

max_standby_streaming_delay:

备机因为接收wal流日志产生查询冲突而取消查询之前的等待时间,设置该参数会在发生冲突时,备库查询不会立即取消,而是等待一个时间后如果还没结束再抛出报错。这个值的大小可以参考备库可能产生的长事务运行时间。

max_standby_archive_delay:

备机因为处理归档的wal日志产生查询冲突而取消查询之前的等待时间,和上面的参数类似。

vacuum_defer_cleanup_age:

指定vacuum延迟清理死亡元组的事务数,vacuum会延迟清除无效的记录,延迟的事务个数通过vacuum_defer_cleanup_age进行设置。即vacuum和vacuum full操作不会立即清理刚刚被删除元组。

生产环境里我们可以根据pg_stat_database和pg_stat_database_confliects视图查看冲突发生的情况,以此来进行如上参数的调整。

技术永无止境,加油吧。

0 人点赞