什么是恢复冲突?
每当恢复进程,因为应用更改会中断在进行的查询处理,无法将来自主服务器的 WAL 信息应用到备用服务器时,就会发生恢复冲突。这些查询冲突不会在主服务器上发生,但它们会发生在流复制中的备用服务器上,因为主服务器对备用服务器上发生的情况了解有限。
有几种类型的恢复冲突:
快照恢复冲突
这是最常见的恢复冲突。
如果 VACUUM 处理过一个表并删除了死元组,则可能会发生快照冲突。删除操作将在备用数据库上重做。现在,备用数据库上的一个查询,可能已在主数据库进行 VACUUM 之前启动(它具有较老的快照),因此它仍然可以看到应删除的元组。这样就构成了快照冲突。
锁恢复冲突
备用服务器上的查询,会对它们正在读取的表进行 ACCESS SHARE 锁定。因此,主服务器上的任何 ACCESS EXCLUSIVE 锁定(与 ACCESS SHARE 冲突),都必须在备用服务器上重放,以防止在表上出现不兼容的操作。PostgreSQL 对与SELECT 冲突的操作都会进行这样的锁定,例如DROP TABLE、TRUNCATE 和许多 ALTER TABLE 语句。如果备用数据库需要在一个查询使用的表上重放这样的锁定,就会出现锁冲突。
缓冲页销恢复冲突
减少 VACUUM 工作量的一种方法是使用 HOT(heap-only tuples)更新。然后,当主节点上的任何查询,访问带有死去的堆元组的页面,并可以在其上获得排他锁时,都可以修剪 HOT 链。PostgreSQL 只会在短时间内保持这样的页面锁定,从而跟主数据库上的处理不会冲突。页面锁定还有其他原因,但这可能是最常见的原因。
当备用服务器需要重放此类排他性页面锁定,并且有查询正在使用该页面时(在 PostgreSQL 术语中为 “页面已上销”),您将收到缓冲页销恢复冲突。页面可能会上销一段时间,例如,在对嵌套循环连接的外侧表进行顺序扫描期间。
当然,HOT 链修剪也会导致快照恢复冲突。
罕见的恢复冲突类型
以下类型的冲突很少见,不太会干扰您:
• 死锁恢复冲突:在共享缓冲页上面重放来自主数据库的 WAL 时,备用数据库上的查询访问该缓冲页会阻塞。PostgreSQL 将立即取消此类查询。
• 表空间恢复冲突:在备用服务器上 temp_tablespaces 中有一个表空间,并且有查询在那里有临时文件。当主服务器上发生 DROP TABLESPACE 操作时,我们会遇到冲突。在这种情况下,PostgreSQL 会取消备用服务器上的所有查询。
• 数据库恢复冲突:如果备用服务器在数据库上有活动会话,DROP DATABASE 操作的复制会发生冲突。在这种情况下,PostgreSQL 会终止与备用服务器上的数据库的所有连接。
备用服务器如何解决恢复冲突?
参数 max_standby_streaming_delay 确定当 WAL 重放遇到恢复冲突时会发生什么情况(有一个类似的参数 max_standby_archive_delay,对于归档恢复进行相同的处理)。PostgreSQL 暂停 WAL 信息的重放,最多 max_standby_streaming_delay 毫秒。如果冲突的查询在该时间之后仍在运行,PostgreSQL 会取消它,并显示类似下面内容的错误消息:
ERROR: canceling statement due to conflict with recovery
DETAIL: User query might have needed to see row versions that must be removed.
详细信息显示,这是由快照恢复冲突产生的错误。
max_standby_streaming_delay 默认值为 30 秒,因此,备用数据库上的查询,有半分钟的“宽限时间”来完成,如果它们引起了恢复冲突,它们会被取消掉。这是底限值 0(PostgreSQL 立即取消查询,无重放延迟)和特殊值 -1(PostgreSQL 从不取消查询,重放延迟可以任意长)之间的中间取值。
查询 pg_stat_database 视图
统计信息视图 pg_stat_database_conflicts 包含了,自上次统计信息重置以来,发生的所有恢复冲突的详细说明。您必须在备用服务器(而不是主服务器)上查看该视图,因为只有那里才会发生恢复冲突。
请注意,此视图不会显示发生的所有恢复冲突,它仅显示导致备用数据库上查询被取消的冲突。我们可以使用下面的查询,来检查恢复冲突:
SELECT conflicts FROM pg_stat_database
WHERE datname = current_database();
一旦发现有许多恢复冲突,您就可以找到那些导致恢复冲突的查询,并调整它们。