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_NULL、DMS_LOCK_SHARE、DMS_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_lock 的 LW_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_SHARE 或 DMS_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 原本是 X,dcs_change_page_status() 会把本地 ctrl->lock_mode 从 X 降为 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.cpp 的 LockBuffer() 中:

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

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

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

9. 总结

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

  • 本地读页需要 DMS_LOCK_SHARE 或 DMS_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 判断并发起的。

相关推荐
电科_银尘8 分钟前
【论文阅读】Deep Image Prior:随机初始化的网络为什么也能重建图像?
网络·论文阅读
2501_930707783 小时前
使用C#代码在 PDF 文档中创建交互操作
pdf·c#·交互
Cry丶10 小时前
ubuntu安装vsftpd
服务器·网络·ubuntu
SHENGWEI胜为13 小时前
交换机端口显示正常,终端却获取不到 IP 地址?从 DHCP 流程定位故障
服务器·网络·测试工具·缓存·通信工程
IMPYLH15 小时前
Linux 系统备份与镜像
linux·服务器·网络
淘气包海鸟16 小时前
水声网络框架
网络·ns3·水
国科安芯17 小时前
星间激光通信终端CANFD控制网络的时延确定性与辐射可靠性
网络·嵌入式硬件·信息与通信·canfd·低轨卫星·商业航天·抗辐射
中屹指纹浏览器svip17 小时前
网络抖动会标记账号异常?分清两种波动
网络
承渊政道17 小时前
Linux网络学习【传输层协议UDP】
linux·网络·网络协议·学习·ubuntu·udp
小此方18 小时前
Linux网络(三十三):告别O(n)噩梦:从select/poll的挣扎到epoll红黑树+回调的降维打击,一文打通IO多路复用
linux·网络·驱动开发