openGauss 资源池化主备页面交互流程梳理

openGauss 资源池化主备页面交互流程梳理

本文整理 openGauss-server 资源池化场景下,主机和备机访问同一数据页时,如何通过 DMS/DRC 协调页面读写权限、传递页面内容、失效共享副本。

涉及代码:

  • openGauss 本地 buffer 入口:D:\project\openGauss-server4\src\gausskernel\storage\buffer\bufmgr.cpp
  • openGauss DMS buffer 适配:D:\project\openGauss-server4\src\gausskernel\ddes\adapter\ss_dms_bufmgr.cpp
  • openGauss DMS callback:D:\project\openGauss-server4\src\gausskernel\ddes\adapter\ss_dms_callback.cpp
  • DMS 页协议:D:\project\DMS\src\dcs\dcs_page.c
  • DMS 消息与 master/owner 协调:D:\project\DMS\src\dms\dms_msg.c
  • DRC 页资源状态:D:\project\DMS\src\drc\drc_page.c

1. 基本概念

资源池化场景中,一个页面同时有本地 buffer 状态和 DMS 全局状态。

本地 openGauss buffer 侧主要看:

  • BufferDesc->content_lock:本地页面内容锁,LW_SHARED 用于读页,LW_EXCLUSIVE 用于改页或失效页。
  • dms_buf_ctrl_t->lock_mode:本实例对该页持有的 DMS 全局页锁,可能是 DMS_LOCK_NULLDMS_LOCK_SHAREDMS_LOCK_EXCLUSIVE
  • dms_buf_ctrl_t->been_loaded:本地 buffer 是否已经加载过有效页面。
  • dms_buf_ctrl_t->is_edp / edp_map:页 owner 转移后,旧 owner 上可能存在的脏页信息。

DMS/DRC 侧主要看:

  • owner:当前全局页面 owner,代表最新页版本所在实例。
  • lock_mode:DRC 记录的全局页锁模式,S 表示可多实例共享读,X 表示单 owner 独占。
  • copy_insts:持有共享副本的实例集合,通常是读过该页并 claim 过 S 的实例。
  • converting / convert_q:正在转换或等待转换的页请求。

需要特别注意:数据库主机不一定是 DRC master,数据库备机也不一定是 page owner。下面为了便于描述,"主机/备机"指数据库角色,"master"指 DRC master,"owner"指 DMS 页 owner。

2. 什么时候会走 DMS 页面交互

资源池化开启后,业务访问 buffer 时,本地 LockBuffer() 会触发 DMS 页权限检查。

入口链路:

text 复制代码
bufmgr.cpp
LockBuffer(buffer, BUFFER_LOCK_SHARE / BUFFER_LOCK_EXCLUSIVE)
    -> DmsReadPage() / DmsReadSegPage()
        -> DmsStartBufferIO()
        -> StartReadPage()
            -> dms_request_page()
                -> dms_request_res_internal()

本地是否需要真正向 DMS 请求,取决于 LockModeCompatible()

text 复制代码
请求本地读页 LW_SHARED:
    本地 DMS_LOCK_SHARE     兼容
    本地 DMS_LOCK_EXCLUSIVE 兼容
    本地 DMS_LOCK_NULL      不兼容,需要 StartReadPage()

请求本地改页 LW_EXCLUSIVE:
    本地 DMS_LOCK_EXCLUSIVE 兼容
    本地 DMS_LOCK_SHARE     不兼容,需要 StartReadPage()
    本地 DMS_LOCK_NULL      不兼容,需要 StartReadPage()

因此主机请求 X 时,主机本地并不知道"备机是否持有 S 副本"。主机只知道自己当前没有足够的本地 DMS 页权限,所以通过 StartReadPage() 向 DMS 请求 DMS_LOCK_EXCLUSIVE。是否需要失效备机副本,是 DRC master 根据 copy_insts 判断出来的。

3. DMS 请求的通用流程

DMS 请求先找该页的 DRC master:

text 复制代码
dms_request_page()
    -> dms_request_res_internal()
        -> drc_get_master_id()
        -> 如果 master 是本实例:dms_ask_master4res_l()
        -> 如果 master 是远端:dms_ask_master4res_r()

master 收到请求后,进入:

text 复制代码
dms_proc_ask_master_for_res()
    -> drc_request_page_owner()
        -> drc_request_page_owner_internal()
            -> drc_enq_req_item()
            -> drc_set_req_result()

drc_set_req_result() 可能返回几类结果:

  • DRC_REQ_OWNER_GRANTED:没有需要找的 owner,请求方可以直接获得权限,通常自己从盘读。
  • DRC_REQ_OWNER_ALREADY_OWNER:请求方已经是 owner,只需要确认或升级本地模式。
  • DRC_REQ_OWNER_CONVERTING:需要找当前 owner 转页或转 owner。
  • DRC_REQ_OWNER_WAITING:前面已有转换中的请求,需要排队等待。

如果请求是 DMS_LOCK_EXCLUSIVE,master 会额外计算需要失效的共享副本:

text 复制代码
result->invld_insts = drc->copy_insts
清掉请求者自己

然后 master 通过 dms_invalidate_share_copy_with_timeout() 给这些实例发送 MSG_REQ_INVALIDATE_SHARE_COPY

4. 场景一:备机持有 S 读副本,主机请求 X

4.1 触发条件

备机之前读过该页,并通过 DMS claim 过 DMS_LOCK_SHARE。DRC master 上大致是:

text 复制代码
drc.lock_mode = DMS_LOCK_SHARE
drc.owner = 某实例
drc.copy_insts 包含备机

主机业务现在要修改该页,调用:

text 复制代码
LockBuffer(buffer, BUFFER_LOCK_EXCLUSIVE)

本地主机如果不是 DMS_LOCK_EXCLUSIVE,则 LockModeCompatible() 不兼容,于是进入:

text 复制代码
StartReadPage(req_mode = DMS_LOCK_EXCLUSIVE)

4.2 主机到 DMS master

text 复制代码
主机 StartReadPage()
    -> dms_request_page(DMS_LOCK_EXCLUSIVE)
    -> dms_request_res_internal()
    -> MSG_REQ_ASK_MASTER_FOR_PAGE

master 调 drc_request_page_owner(),发现请求是 X,同时 copy_insts 中有备机,于是先失效备机副本。

4.3 master 失效备机 S copy

text 复制代码
master
    -> dms_invalidate_share_copy_with_timeout()
    -> MSG_REQ_INVALIDATE_SHARE_COPY 发给备机

备机 DMS worker
    -> dms_proc_invld_req()
    -> CBInvalidatePage()

备机 CBInvalidatePage() 做的事情:

text 复制代码
BufTableLookup 找本地 buffer
    -> PinBuffer 防止淘汰
    -> 等待 IO 完成
    -> 校验 buffer tag 和 BM_VALID
    -> SSLWLockAcquireTimeout(content_lock, LW_EXCLUSIVE)
    -> buf_ctrl->lock_mode = DMS_LOCK_NULL
    -> 清理 seg_fileno / seg_blockno / lsn_on_disk 等 DMS 元信息
    -> LWLockRelease(content_lock)
    -> ReleaseBuffer
    -> 给 master 回 ACK

这里的关键是:备机 DMS worker 要拿本地 content_lockLW_EXCLUSIVE,必须等备机业务读线程释放本地 LW_SHARED。因此主机拿全局 X 前,会等待备机当前读页面的本地内容锁释放。

4.4 owner 转移或确认

共享副本失效后,根据 owner 情况分支:

如果主机已经是 owner:

text 复制代码
master -> 主机:MSG_ACK_ALREADY_OWNER
主机 dcs_handle_ack_already_owner()
    -> 本地 ctrl->lock_mode = DMS_LOCK_EXCLUSIVE
    -> dms_claim_ownership(mode=X)

如果 owner 在其他实例:

text 复制代码
master -> owner:MSG_REQ_ASK_OWNER_FOR_PAGE, req=X
owner dcs_owner_transfer_page()
    -> CBEnterLocalPage(EXCLUSIVE)
    -> dcs_change_page_status()
    -> MSG_ACK_PAGE_READY 发给主机
主机 dcs_handle_ack_page_ready()
    -> 拷贝页面或确认权限
    -> dms_claim_ownership(mode=X)

master 处理 claim:

text 复制代码
dms_proc_claim_ownership_req()
    -> drc_claim_page_owner()
        -> drc_convert_page_owner()
            -> drc.owner = 主机
            -> drc.lock_mode = DMS_LOCK_EXCLUSIVE
            -> drc.copy_insts = 0

如果旧 owner 页是脏的,DMS 会记录 EDP 信息,避免丢失最新脏页版本。

4.5 时序图

当前 owner 备机 sharer DRC master 主机 requester 当前 owner 备机 sharer DRC master 主机 requester #mermaid-svg-020Dqo6Cg0AtohVE{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-020Dqo6Cg0AtohVE .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-020Dqo6Cg0AtohVE .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-020Dqo6Cg0AtohVE .error-icon{fill:#552222;}#mermaid-svg-020Dqo6Cg0AtohVE .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-020Dqo6Cg0AtohVE .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-020Dqo6Cg0AtohVE .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-020Dqo6Cg0AtohVE .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-020Dqo6Cg0AtohVE .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-020Dqo6Cg0AtohVE .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-020Dqo6Cg0AtohVE .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-020Dqo6Cg0AtohVE .marker{fill:#333333;stroke:#333333;}#mermaid-svg-020Dqo6Cg0AtohVE .marker.cross{stroke:#333333;}#mermaid-svg-020Dqo6Cg0AtohVE svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-020Dqo6Cg0AtohVE p{margin:0;}#mermaid-svg-020Dqo6Cg0AtohVE .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-020Dqo6Cg0AtohVE text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-020Dqo6Cg0AtohVE .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-020Dqo6Cg0AtohVE .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-020Dqo6Cg0AtohVE .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-020Dqo6Cg0AtohVE .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-020Dqo6Cg0AtohVE #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-020Dqo6Cg0AtohVE .sequenceNumber{fill:white;}#mermaid-svg-020Dqo6Cg0AtohVE #sequencenumber{fill:#333;}#mermaid-svg-020Dqo6Cg0AtohVE #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-020Dqo6Cg0AtohVE .messageText{fill:#333;stroke:none;}#mermaid-svg-020Dqo6Cg0AtohVE .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-020Dqo6Cg0AtohVE .labelText,#mermaid-svg-020Dqo6Cg0AtohVE .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-020Dqo6Cg0AtohVE .loopText,#mermaid-svg-020Dqo6Cg0AtohVE .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-020Dqo6Cg0AtohVE .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-020Dqo6Cg0AtohVE .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-020Dqo6Cg0AtohVE .noteText,#mermaid-svg-020Dqo6Cg0AtohVE .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-020Dqo6Cg0AtohVE .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-020Dqo6Cg0AtohVE .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-020Dqo6Cg0AtohVE .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-020Dqo6Cg0AtohVE .actorPopupMenu{position:absolute;}#mermaid-svg-020Dqo6Cg0AtohVE .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-020Dqo6Cg0AtohVE .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-020Dqo6Cg0AtohVE .actor-man circle,#mermaid-svg-020Dqo6Cg0AtohVE line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-020Dqo6Cg0AtohVE :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} alt主机已经是 ownerowner 在其他实例 LockBuffer(EXCLUSIVE)1DmsReadPage ->> StartReadPage(X)2MSG_REQ_ASK_MASTER_FOR_PAGE, req=X3查 DRC, lock_mode=S, copy_insts 包含备机4MSG_REQ_INVALIDATE_SHARE_COPY5CBInvalidatePage()6等业务读线程释放 content_lock(S)7拿 content_lock(X), lock_mode=NULL8ACK_INVLDT_SHARE_COPY9清理 copy_insts10MSG_ACK_ALREADY_OWNER11MSG_REQ_ASK_OWNER_FOR_PAGE, req=X12CBEnterLocalPage(EXCLUSIVE)13dcs_change_page_status()14MSG_ACK_PAGE_READY15MSG_REQ_CLAIM_OWNER, mode=X16owner=主机, lock_mode=X, copy_insts=017

5. 场景二:主机持有 S,备机请求读页

5.1 触发条件

主机当前持有该页共享版本,DRC 可能是:

text 复制代码
drc.lock_mode = DMS_LOCK_SHARE
drc.owner = 主机
drc.copy_insts 可能为空,也可能包含其他读副本

备机业务执行读页:

text 复制代码
LockBuffer(buffer, BUFFER_LOCK_SHARE)

如果备机本地没有 DMS_LOCK_SHAREDMS_LOCK_EXCLUSIVE,就会进入 StartReadPage(DMS_LOCK_SHARE)

5.2 DMS master 找 owner 传页

备机请求 S 到 master:

text 复制代码
备机 -> master:MSG_REQ_ASK_MASTER_FOR_PAGE, req=S

master 查 DRC,发现当前 owner 是主机。对于 page 资源,备机想读不仅需要权限,还需要拿到最新页面内容,所以 master 会让 owner 传页:

text 复制代码
master -> 主机 owner:MSG_REQ_ASK_OWNER_FOR_PAGE, req=S

主机 owner 收到后:

text 复制代码
dms_proc_ask_owner_for_res()
    -> dcs_owner_transfer_page()
        -> CBEnterLocalPage(SHARE)
        -> dcs_change_page_status()
        -> dcs_owner_transfer_page_ack(MSG_ACK_PAGE_READY)

如果 owner 原本是 Xdcs_change_page_status() 会把本地 ctrl->lock_modeX 降为 S。如果 owner 已经是 S,则保持 S

5.3 备机加载页面并 claim S

备机收到 MSG_ACK_PAGE_READY

text 复制代码
dcs_handle_ack_page_ready()
    -> dcs_handle_page_from_owner()
        -> memcpy 页面内容到本地 buffer
        -> ctrl->lock_mode = DMS_LOCK_SHARE
        -> been_loaded = true
    -> dms_claim_ownership(mode=S)

master 处理 claim:

text 复制代码
drc_convert_page_owner()
    -> lock_mode = DMS_LOCK_SHARE
    -> owner 通常保持主机
    -> copy_insts 加入备机

此后主机和备机都可以读这个页。后续任意实例要拿 X,都必须先失效 copy_insts 中的共享副本。

5.4 时序图

主机 owner DRC master 备机 requester 主机 owner DRC master 备机 requester #mermaid-svg-YImzDICspYjpHXAt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-YImzDICspYjpHXAt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-YImzDICspYjpHXAt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-YImzDICspYjpHXAt .error-icon{fill:#552222;}#mermaid-svg-YImzDICspYjpHXAt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-YImzDICspYjpHXAt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-YImzDICspYjpHXAt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-YImzDICspYjpHXAt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-YImzDICspYjpHXAt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-YImzDICspYjpHXAt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-YImzDICspYjpHXAt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-YImzDICspYjpHXAt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-YImzDICspYjpHXAt .marker.cross{stroke:#333333;}#mermaid-svg-YImzDICspYjpHXAt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-YImzDICspYjpHXAt p{margin:0;}#mermaid-svg-YImzDICspYjpHXAt .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-YImzDICspYjpHXAt text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-YImzDICspYjpHXAt .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-YImzDICspYjpHXAt .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-YImzDICspYjpHXAt .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-YImzDICspYjpHXAt .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-YImzDICspYjpHXAt #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-YImzDICspYjpHXAt .sequenceNumber{fill:white;}#mermaid-svg-YImzDICspYjpHXAt #sequencenumber{fill:#333;}#mermaid-svg-YImzDICspYjpHXAt #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-YImzDICspYjpHXAt .messageText{fill:#333;stroke:none;}#mermaid-svg-YImzDICspYjpHXAt .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-YImzDICspYjpHXAt .labelText,#mermaid-svg-YImzDICspYjpHXAt .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-YImzDICspYjpHXAt .loopText,#mermaid-svg-YImzDICspYjpHXAt .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-YImzDICspYjpHXAt .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-YImzDICspYjpHXAt .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-YImzDICspYjpHXAt .noteText,#mermaid-svg-YImzDICspYjpHXAt .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-YImzDICspYjpHXAt .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-YImzDICspYjpHXAt .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-YImzDICspYjpHXAt .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-YImzDICspYjpHXAt .actorPopupMenu{position:absolute;}#mermaid-svg-YImzDICspYjpHXAt .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-YImzDICspYjpHXAt .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-YImzDICspYjpHXAt .actor-man circle,#mermaid-svg-YImzDICspYjpHXAt line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-YImzDICspYjpHXAt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} LockBuffer(SHARE)1DmsReadPage ->> StartReadPage(S)2MSG_REQ_ASK_MASTER_FOR_PAGE, req=S3查 DRC, owner=主机4MSG_REQ_ASK_OWNER_FOR_PAGE, req=S5CBEnterLocalPage(SHARE)6如果本地是 X, 降为 S7MSG_ACK_PAGE_READY, 带页面内容8拷贝页面到本地 buffer, lock_mode=S9MSG_REQ_CLAIM_OWNER, mode=S10lock_mode=S, 备机加入 copy_insts11

6. 场景三:主机没有页面,备机请求读页

"主机没有页面"还要细分两种情况:DRC 没 owner,或者 DRC 有 owner 但 owner 本地 buffer 已经换出。

6.1 DRC 没有 owner

如果 master 查 DRC 发现:

text 复制代码
drc.owner = INVALID
drc.last_edp 不需要恢复或磁盘已经足够新

drc_get_page_no_owner() 返回 DRC_REQ_OWNER_GRANTED。master 回:

text 复制代码
MSG_ACK_GRANT_OWNER, master_grant=true

备机处理:

text 复制代码
dcs_handle_ack_need_load()
    -> dcs_handle_prepare_need_load()
        -> master_grant=true 时 granted_mode = DMS_LOCK_EXCLUSIVE
        -> ctrl->lock_mode = DMS_LOCK_EXCLUSIVE
        -> 设置 DMS_BUF_NEED_LOAD
    -> dms_claim_ownership(mode=X)

TerminateReadPage()
    -> 从共享盘读页

所以冷页首读虽然业务请求是 S,但 DMS 可能把首个读者授为 X owner。这样可以让该实例成为最新页 owner,后续其他读者再从它传页或共享。

6.2 DRC 有 owner,但 owner 本地没有页面

如果 DRC 上仍有 owner,但 owner 本地 buffer 查不到或已经换出,owner 侧:

text 复制代码
dcs_owner_transfer_page()
    -> read_local_page4transfer()
    -> ctrl == NULL
    -> dcs_owner_send_granted_ack()

此时 owner 回:

text 复制代码
MSG_ACK_GRANT_OWNER, master_grant=false

请求方处理时,granted_mode 保持原请求模式。如果备机请求 S,则本地 ctrl->lock_mode = DMS_LOCK_SHARE,然后从共享盘读页,再 claim S

这个分支和"完全无 owner"不同:无 owner 且 master_grant=true 时,请求方会被授予 X;owner 本地无页且 master_grant=false 时,请求方通常按原始请求模式拿 S

6.3 时序图

共享磁盘 主机 owner DRC master 备机 requester 共享磁盘 主机 owner DRC master 备机 requester #mermaid-svg-g0USUVb5uV8meHAp{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-g0USUVb5uV8meHAp .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-g0USUVb5uV8meHAp .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-g0USUVb5uV8meHAp .error-icon{fill:#552222;}#mermaid-svg-g0USUVb5uV8meHAp .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-g0USUVb5uV8meHAp .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-g0USUVb5uV8meHAp .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-g0USUVb5uV8meHAp .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-g0USUVb5uV8meHAp .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-g0USUVb5uV8meHAp .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-g0USUVb5uV8meHAp .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-g0USUVb5uV8meHAp .marker{fill:#333333;stroke:#333333;}#mermaid-svg-g0USUVb5uV8meHAp .marker.cross{stroke:#333333;}#mermaid-svg-g0USUVb5uV8meHAp svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-g0USUVb5uV8meHAp p{margin:0;}#mermaid-svg-g0USUVb5uV8meHAp .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-g0USUVb5uV8meHAp text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-g0USUVb5uV8meHAp .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-g0USUVb5uV8meHAp .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-g0USUVb5uV8meHAp .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-g0USUVb5uV8meHAp .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-g0USUVb5uV8meHAp #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-g0USUVb5uV8meHAp .sequenceNumber{fill:white;}#mermaid-svg-g0USUVb5uV8meHAp #sequencenumber{fill:#333;}#mermaid-svg-g0USUVb5uV8meHAp #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-g0USUVb5uV8meHAp .messageText{fill:#333;stroke:none;}#mermaid-svg-g0USUVb5uV8meHAp .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-g0USUVb5uV8meHAp .labelText,#mermaid-svg-g0USUVb5uV8meHAp .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-g0USUVb5uV8meHAp .loopText,#mermaid-svg-g0USUVb5uV8meHAp .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-g0USUVb5uV8meHAp .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-g0USUVb5uV8meHAp .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-g0USUVb5uV8meHAp .noteText,#mermaid-svg-g0USUVb5uV8meHAp .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-g0USUVb5uV8meHAp .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-g0USUVb5uV8meHAp .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-g0USUVb5uV8meHAp .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-g0USUVb5uV8meHAp .actorPopupMenu{position:absolute;}#mermaid-svg-g0USUVb5uV8meHAp .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-g0USUVb5uV8meHAp .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-g0USUVb5uV8meHAp .actor-man circle,#mermaid-svg-g0USUVb5uV8meHAp line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-g0USUVb5uV8meHAp :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} altDRC 没有 ownerDRC 有 owner, 但主机本地没有页 LockBuffer(SHARE)1MSG_REQ_ASK_MASTER_FOR_PAGE, req=S2MSG_ACK_GRANT_OWNER, master_grant=true3ctrl.lock_mode=X, NEED_LOAD4TerminateReadPage 从盘读页5MSG_REQ_CLAIM_OWNER, mode=X6备机成为 X owner7MSG_REQ_ASK_OWNER_FOR_PAGE, req=S8MSG_ACK_GRANT_OWNER, master_grant=false9ctrl.lock_mode=S, NEED_LOAD10从盘读页11MSG_REQ_CLAIM_OWNER, mode=S12备机加入 copy_insts13

7. 备机失效时 content_lock 的生命周期

备机业务读线程和 DMS 失效线程使用的是同一个本地锁:

text 复制代码
buf_desc->content_lock

但生命周期不同。

7.1 备机业务读线程的 S 锁

text 复制代码
ReadBuffer 或命中 buffer
    -> LockBuffer(buffer, BUFFER_LOCK_SHARE)
        -> 拿 content_lock 的 LW_SHARED
        -> DmsReadPage() 确认或获取 DMS_LOCK_SHARE
    -> 上层读取 Page 内容
    -> LockBuffer(buffer, BUFFER_LOCK_UNLOCK)
        -> 释放 LW_SHARED

这个 LW_SHARED 的生命周期由上层访问页面逻辑控制。注意,buffer pin 和 content lock 不是一回事,释放 content lock 不等于一定释放 pin。

7.2 DMS 失效线程的 X 锁

主机请求 X 时,备机 DMS worker 为了失效本地 S copy,会临时拿 LW_EXCLUSIVE

text 复制代码
CBInvalidatePage()
    -> 找到 buffer
    -> PinBuffer
    -> 等待 IO 完成
    -> SSLWLockAcquireTimeout(content_lock, LW_EXCLUSIVE)
    -> buf_ctrl->lock_mode = DMS_LOCK_NULL
    -> LWLockRelease(content_lock)
    -> ReleaseBuffer

这个 LW_EXCLUSIVE 只覆盖"把本地 DMS 页权限置空"的短临界区。它不会持有到主机修改完成,也不会持有到主机释放全局 X

如果备机业务读线程还持有 LW_SHARED,失效线程会在 SSLWLockAcquireTimeout() 等待。只有业务读线程释放本地读锁,失效才能成功,master 才能继续授予主机全局 X

失效成功后,页面内容可能仍留在备机 buffer 中,但 DMS 语义上已经不可用。备机后续再读同一页时,因为本地 buf_ctrl->lock_mode = DMS_LOCK_NULL,会重新走 StartReadPage() 向 DMS 申请 S,不能直接使用旧副本。

8. 备机读与主机写并发时的防死锁处理

LockBuffer() 中有一段专门处理备机读页失败重试的逻辑。典型问题是:

text 复制代码
两个备机读线程持有本地 content_lock 的 LW_SHARED
主机请求 X,需要 DMS worker 在备机拿 LW_EXCLUSIVE 做 invalidate
备机读线程又在等待主机或 owner 返回最新页

这会形成等待窗口。代码中处理方式是:备机第一次读页失败后,重试时临时把本地锁模式从 BUFFER_LOCK_SHARE 改为 BUFFER_LOCK_EXCLUSIVE,成功后再 downgrade 回 SHARED

对应逻辑在 bufmgr.cppLockBuffer() 中:

text 复制代码
如果 DmsReadPage() 返回失败
    -> 释放本地 content_lock
    -> 如果是备机且第一次重试
        -> mode = BUFFER_LOCK_EXCLUSIVE
    -> retry

如果重试成功且原始请求是 SHARE
    -> LWLockDowngrade(content_lock)

这样可以让备机读线程不要长期占住共享锁,为主机发来的 invalidate 留出拿 LW_EXCLUSIVE 的窗口。

9. 总结

主机或备机是否需要通过 DMS 交互,不是由数据库角色直接决定,而是由本地 buf_ctrl->lock_mode 和请求的本地锁模式决定:

  • 本地读页需要 DMS_LOCK_SHAREDMS_LOCK_EXCLUSIVE
  • 本地改页需要 DMS_LOCK_EXCLUSIVE
  • 本地权限不够时,LockBuffer() 会进入 DmsReadPage() / StartReadPage() 请求 DMS。

DMS master 决定跨实例交互方式:

  • 请求 S 且有 owner:找 owner 传页,请求方 claim S,加入 copy_insts
  • 请求 S 且无 owner:master 可直接 grant,请求方可能成为 X owner 并从盘读。
  • 请求 X 且存在 S copy:先失效 copy_insts 中的共享副本,再授予或转移 X owner
  • 请求 X 且 owner 在其他实例:让 owner 转页或转 owner,旧 owner 脏页通过 EDP 记录。

主机请求 X 时,主机本身并不知道备机持有页面读锁;主机只发现本地没有全局 X 权限。备机是否需要被失效,是 DRC master 根据 copy_insts 判断并发起的。

相关推荐
Dlrb12111 小时前
Linux网络编程-网络基础概念(IP, UDP协议)
linux·服务器·网络·网络基础·端口号·ip协议·udp协议
shushangyun_1 小时前
汽车服务行业B2B平台+AI解决方案哪家专业:2026年最新测评
java·运维·网络·数据库·人工智能·汽车
一RTOS一2 小时前
东土科技:智能制造系统高性能工业网络解决方案揭榜挂帅项目正式验收达标
网络·科技·制造
森G2 小时前
64、完善聊天室程序(TLV拓展)---------网络编程
网络·c++·tcp/ip
potion()2 小时前
浏览器用户画像分析-大屏静态布局制作+数据接入+交互设置
交互·助睿数智·商业数据分析
LONGZETECH2 小时前
无人机仿真教学软件选型实战:5 个硬核技术维度,避开实训建设踩坑
3d·无人机·交互·cocos2d
专业机床数据采集2 小时前
基于 Wireshark 抓包逆向设备通信协议,并用 C# UDP协议跨平台 实现宝元数控程序列表读取、上传、下载和删除
网络·测试工具·wireshark·程序传输·宝元数控·dnc·数控程序传输
信也科技布道师3 小时前
从Istio 503 NC 错误深入理解 Mesh 路由全链路原理
java·服务器·网络