暂无图片
暂无图片
1
暂无图片
暂无图片
暂无图片

KingbaseES备库查询与VACUUM的"战争":深入剖析流复制冲突的两种解法

原创 周波 3天前
63

一、问题现象:一个令人困惑的 FATAL 错误

  在使用KingbaseES流复制架构的读写分离场景中,备库上运行的查询有时会突然中断,并抛出如下错误信息:

FATAL: terminating connection due to conflict with recovery
Detail: User query might have needed to see row versions that must by removed.

  这个错误意味着备库的后台恢复进程与用户查询发生了冲突,导致查询被强制终止。接下来,我们将深入剖析这一问题的根本原因,并详细探讨两种解决措施及其背后的代价。

二、报错原因:VACUUM与长查询的冲突

  该错误的根本原因在于主库的VACUUM清理操作与备库正在执行的长查询之间产生了冲突。
  具体来说,当备库执行一个长时间运行的查询时,若此时主库正在进行VACUUM操作以清理死元组(Dead Tuples),备库的恢复进程会怀疑:主库的这次VACUUM可能会清理掉当前备库查询仍需读取的旧版本元组。为了维护数据一致性,KingbaseES默认会采取保护措施——强制取消该长查询,从而触发上述冲突错误。
  简而言之:主库想删(VACUUM),备库还想看(长查询),数据库选择保护数据一致性,牺牲了查询。

三、解决措施一:hot_standby_feedback=on —— 牺牲表膨胀,保查询稳定

3.1 工作原理

  开启hot_standby_feedback=on后,备库会通过流复制协议定期(间隔时间不超过 wal_receiver_status_interval参数的设置值)向主库发送自身当前最小的活跃事务ID(xmin)。

  主库收到这个反馈信息后,在执行VACUUM时,会保留该事务ID之后产生的所有死亡元组,不敢轻易清理回收。这样一来,备库长查询所需的旧版本数据始终不会被清理,冲突自然就被避免了。

3.2 优点

  • 备库WAL日志应用正常:由于不延迟WAL应用,备库数据同步及时,主备数据一致性有保障。
  • 长查询稳定运行:不会因为VACUUM冲突而被强制终止,对备库上的分析、报表类业务友好。

3.3 隐患:表膨胀显著

  这是该方案最突出的代价。设置hot_standby_feedback=on存在一个表膨胀的隐患:备库最早活跃查询发起之后,主库所产生的所有死元组(包括相关表及其它所有表)均不会被回收重用
  换言之,只要备库有长查询未结束,无论主库上哪个表产生了死元组,VACUUM都无法清理。在备库查询时间长、主库更新频繁的场景中,这种表膨胀会额外显著,可能导致磁盘空间急剧消耗、数据库性能严重下降。

3.4 适用场景

  适合备库查询重要性高、对数据一致性要求严格,且磁盘空间充裕、可以承受一定表膨胀的场景。

四、解决措施二:调整max_standby_streaming_delay —— 保VACUUM效率,牺牲一致性与实时性

4.1 工作原理

  设置max_standby_streaming_delay参数(例如 ‘60s’)后,备库在应用WAL日志时会容忍一个最大延迟时间。当备库有长查询正在执行,且即将应用可能引发冲突的VACUUM日志时,备库会延迟应用该VACUUM操作及其之后产生的所有WAL日志,给查询留出执行窗口。
  若查询在max_standby_streaming_delay设定的时间内完成,则延迟结束,WAL 正常应用;若超时后查询仍未完成,则不管该VACUUM操作是否会回收重用该查询所需要的旧版元组,备库都会强制终止查询,并报出同样的FATAL错误,随后积压的WAL日志会按顺序在备库中完成应用。

4.2 优点

  • 主库VACUUM正常运作:死元组可以及时回收重用,表膨胀问题得到有效控制。
  • 短查询几乎不受影响:只要查询能在延迟窗口内完成,就不会被中断。

4.3 隐患:主备数据不一致且延迟积压严重

  该方案存在两个相互关联的问题:

  • 主备数据存在不一致性问题:备库发起查询后,针对主库上发生的第一个VACUUM操作(不管是针对相关表还是其它表)及之后产生的所有WAL日志(不论是相关表还是其它表)均不会在备库上应用。这意味着备库的数据会落后于主库,读写分离场景下,备库读到的可能是“陈旧”的数据。
  • 在主库更新频繁、备库只读查询耗时长的场景中,这种数据差异会特别显著:一旦开始延迟,后续所有变更(无论哪个表)都会积压在备库,直到查询结束或超时,延迟规模会迅速扩大。

4.4 适用场景

  适合存储空间有限、表膨胀不可接受,且可以容忍一定时间内主备数据不一致、备库查询可接受被中断的业务场景。

五、问题复现验证

  为了更直观地理解两种方案的行为差异,我们可以在主备架构(流复制)中进行复现:

5.1 复现场景一:hot_standby_feedback=on,max_standby_streaming_delay=0

  • 首先在备库中开启一个长查询;
  • 随后在主库上对相关表或其它表执行变更操作,产生死亡元组;
  • 随后对表执行 VACUUM 操作。

  观察结果:会发现相关死亡元组并不会被回收重用,表空间持续增长,验证了表膨胀隐患

5.2 复现场景二:hot_standby_feedback=off,max_standby_streaming_delay=‘60s’

  • 首先在备库中开启一个长查询;
  • 随后在主库上对相关表或其它表执行变更操作,产生死亡元组,相关变更会正常同步至备库;
  • 随后对表执行 VACUUM 操作,死亡元组会被回收重用;
  • 随后再次对相关表或其它表执行变更操作,此时该变更并不会在备库中应用,开始出现数据不一致情况;
  • 若 60 秒过后,该长查询仍未结束,则会报出同样的 FATAL 错误,且积压的 WAL 日志会按顺序在备库中完成应用。

六、总结与权衡:读写分离架构中的两难抉择

  在读写分离架构中,表膨胀与主备数据不一致是悬在头上的两把剑。两种方案对应着不同的取舍:

对比维度 hot_standby_feedback=on max_standby_streaming_delay调大
备库WAL应用 正常,延迟低或无延迟 可能长时间延迟
主备数据一致性 存在数据不一致窗口
表膨胀风险 高(所有表均受影响) 低(VACUUM 正常)
备库长查询稳定性 稳定,不会被终止 可能超时被终止
核心代价 表膨胀 主备数据不一致

  结论:没有完美的解决方案,需要在表膨胀与主备数据不一致之间做权衡取舍。

最后修改时间:2026-09-04 10:26:11
「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论