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 传页,请求方 claimS,加入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 判断并发起的。