openGauss同步提交

openGauss 没有用 PG 原生的 SyncRepWakeQueue 批量唤醒

PG 原生的等待模型

PostgreSQL 使用的是共享内存双向链表队列(WalSndCtlData.SyncRepQueueNUM_SYNC_REP_WAIT_MODE):

后端进入等待:

  1. 申请 SyncRepLock

  2. 把自己插入 SyncRepQueuemode(按 LSN 排序)

  3. syncRepState = SYNC_REP_WAITING

  4. 释放 SyncRepLock

  5. 等自己的 latch

walsender 更新 LSN 后:

  1. 申请 SyncRepLock

  2. 调用 SyncRepWakeQueue() ------ 遍历队列,把 LSN 满足的所有 backend 一次性全部唤醒

  3. 释放 SyncRepLock

这样 N个backend排队,一次唤醒全部,没有惊群。

openGauss 的等待模型

openGauss 没有用这个队列机制,而是引入了一个串行锁 walSyncRepWaitLock:

// syncrep.cpp:496-511

if (LWLockAcquireOrWait(walSyncRepWaitLock, LW_EXCLUSIVE)) {

WalSndCtl->syncWaitProc = t_thrd.proc; // 记录当前等锁的人

ResetLatch(&t_thrd.proc->procLatch);

WaitLatch(&t_thrd.proc->procLatch, ..., 1000L); // 最多等1秒

WalSndCtl->syncWaitProc = NULL;

LWLockRelease(walSyncRepWaitLock);

}

同一时刻只有 1 个 backend 持有这个锁、处于"可被唤醒"状态,其他 N-1 个 backend 阻塞在锁上。

唤醒链路是:

walsender 更新LSN → SyncRepWakeBackend() → SetLatch(syncWaitProc->latch)

→ 那1个backend醒来,检查LSN,释放锁

→ 下一个backend获得锁,成为新的syncWaitProc

→ 等待下次LSN更新再被唤醒(或等1秒超时)

性能问题确实存在

惊群问题:是的,有。每个 walsender 刷新 LSN 后只唤醒 1 个 backend,如果 N 个 backend 都在等,需要 N 次 LSN

更新(或1秒超时)才能轮完。

对比 PG 原生方案:

┌──────────┬──────────────────────────────┬────────────────────────────────┐

│ 维度 │ PG 原生 (SyncRepQueue) │ openGauss (walSyncRepWaitLock) │

├──────────┼──────────────────────────────┼────────────────────────────────┤

│ 等待方式 │ 共享内存链表队列 │ 串行锁排队 │

├──────────┼──────────────────────────────┼────────────────────────────────┤

│ 唤醒方式 │ 一次唤醒所有满足LSN的backend │ 只唤醒当前持锁的1个backend │

├──────────┼──────────────────────────────┼────────────────────────────────┤

│ 并发等待 │ N个backend同时等latch │ 只有1个在等,其余阻塞锁 │

├──────────┼──────────────────────────────┼────────────────────────────────┤

│ 惊群 │ 无 │ 有(逐个唤醒) │

├──────────┼──────────────────────────────┼────────────────────────────────┤

│ 适用场景 │ 通用 │ 特定优化场景 │

└──────────┴──────────────────────────────┴────────────────────────────────┘

为什么 openGauss 这么改?

这段代码注释说得很清楚(syncrep.cpp:336):

▎ "Each proc try to lock walSyncRepWaitLock, and only one backend can get that lock, other backends will wait for it. The backend

▎ which get lock will wait latch for a while. If it is waked by WalSnder or wait time out, it will release lock. And all backends

▎ will reloop again."

这是 openGauss 针对分布式场景(ENABLE_MULTIPLE_NODES 等)做的特殊改造。核心原因是:

  1. PG 的 SyncRepQueue 机制是为单机设计的,在分布式架构下,多个 DN 之间需要同步协调,批量队列唤醒的语义不够清晰

  2. 串行锁简化了状态机------只有"持有锁的那个人"需要关心 LSN 是否满足,其他人只是排队等锁,避免了多 backend 同时被唤醒后的竞争

  3. 1秒超时兜底------即使某个 backend 拿锁后没被唤醒,最多等1秒就会醒来重新参与下一轮竞争

但你说得对,确实有性能隐患

在高并发同步复制场景下(比如大量 backend 同时 COMMIT 且都要求 sync replication):

  • 每个 backend 需要等前面一个被唤醒 + 检查 + 释放锁,才能轮到下一个

  • 串行化程度高,延迟会随等待 backend 数量线性增长

  • PG 原生方案是 O(1) 唤醒所有,而 openGauss 是 O(N)

这是一个正确性优先于性能的设计取舍,在分布式场景下可能是合理的,但单机高并发场景下确实不如 PG 原生方案高效。

相关推荐
这个DBA有点耶1 小时前
云数据库半夜卡顿,CPU才30%?深挖云盘IOPS突发限制与内核挂起机制
数据库·mysql·云原生
无言听风雨丶2 小时前
Redis 运维实战与排错
运维·数据库·redis
小天源3 小时前
智叟云集系统 · 架构与职责分层
服务器·前端·数据库
herinspace4 小时前
管家婆实用贴-SQL 2008如何修改成混合模式
服务器·数据库·sql·管家婆软件·财务软件·合肥管家婆软件
打螺丝的小李4 小时前
制造企业质量管理系统的三类产品形态:分别解决什么问题
数据库·制造
xixingzhe24 小时前
MySQL 锁表故障排查与处理
数据库·mysql
真上帝的左手5 小时前
30. 技术专题-‌应用框架组件-关系型数据库-MySQL-Mybatis
数据库·分布式·mysql·中间件·mybatis
为思念酝酿的痛5 小时前
MySQL表的连接
数据库·mysql
数字新视界5 小时前
机房动环监控系统在提升数据中心能效与安全中的应用探索
大数据·服务器·数据库·动环监控系统·动力与环境监控系统
bksczm6 小时前
MySQL之存储引擎
数据库·mysql