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 原生方案高效。

相关推荐
bksczm1 小时前
MySQL进阶篇之备份(下半篇)
数据库·mysql
计算机就业还在扩大1 小时前
Redis中的setnx是原子性的?
数据库·redis·缓存
updayday8541 小时前
供应商补交资质文件时,采购难以核对缺件和新版本 - Ping64 文件收集
大数据·网络·数据库·安全·智能路由器
PaperData1 小时前
2012-2025年省市区县政府周边的夜间灯光亮度及是否加班数据
数据库
Light602 小时前
四款云端 Agent 的安全边界画在三个地方
大数据·数据库·安全·个人agent·安全边界·提示注入·grok bot
IvorySQL2 小时前
PostgreSQL 日报| pg_vectorsearch 开源向量搜索发布(10 月 14 日)
数据库·人工智能·postgresql·开源·区块链
m0_587383002 小时前
碰碰好评卡功能落地全流程实战指南 附核心逻辑与常见问题解决方案
java·前端·数据库·后端·struts·需求分析
Y3815326622 小时前
SerpBase + RAG 实战:用搜索 API 给知识库接上实时数据,回答不再过期
java·服务器·数据库
骑着蜗牛撵大象3272 小时前
SQL 编辑器不是高级记事本:KStudio 与 KSQL Developer 里的建表与增删改查手法
数据库·sql·mysql·编辑器·crud·执行计划