| 需求 | 描述 | 傳統交易鎖困境 |
| 資源可見性 | 應用間共享的邏輯鎖 | 交易鎖僅在交易內可見 |
| 異常釋放 | 程式異常終止自動釋放 | 交易回滾可釋放,但進程殘留風險 |
| 交易獨立 | 中途 COMMIT 不釋放會話鎖 | 核心痛點:交易鎖隨 COMMIT 釋放 |
| 非阻塞檢查 | lock() 立即返回成功/失敗 | 傳統 SELECT FOR UPDATE 會等待/逾時 |
三、解決方案架構:鎖表 + SKIP LOCKED DATA
1. 鎖表設計
CREATE TABLE LockTable (
ResourceId CHAR(10) NOT NULL,
CONSTRAINT PK_LockTable PRIMARY KEY (ResourceId)
);
-- 範例:兩個全文索引資源
INSERT INTO LockTable VALUES('INDEX_1');
INSERT INTO LockTable VALUES('INDEX_2');
2. 協議介面
lock(ResourceId):嘗試獲取資源會話鎖,立即返回授予/拒絕
unlock():釋放當前持有的會話鎖
3. 關鍵實現:子執行緒 + 獨立連線 + SKIP LOCKED DATA
-- 子執行緒 SQL:lock() 核心邏輯
DECLARE C1 CURSOR FOR
SELECT ResourceId FROM LockTable
WHERE ResourceId = :resourceId
FOR UPDATE WITH CS SKIP LOCKED DATA;
OPEN C1;
FETCH C1;
IF SQLCODE = 100 (NO_DATA_FOUND) THEN
-- 列被其他應用鎖定,或不存在
RESULT = LOCK_DENIED;
ELSE
-- 成功獲取列的更新鎖,持有至 unlock() 或程式結束
RESULT = LOCK_GRANTED;
-- 子執行緒等待主執行緒終止信號
WAIT_FOR_MAIN_THREAD_SIGNAL();
END IF;
-- 子執行緒 SQL:unlock() 邏輯
CLOSE C1;
ROLLBACK WORK; -- 釋放更新鎖,使列對其他應用可見
四、核心機制深度解析
1. 為什麼需要子執行緒 + 獨立連線?
- 主應用交易 COMMIT/ROLLBACK 不得影響 會話鎖持有
- 子執行緒獨立連線的交易週期 = 會話鎖生命週期
- 主執行緒可自由進行業務交易提交/回滾
2. SKIP LOCKED DATA 關鍵作用
FOR UPDATE WITH CS SKIP LOCKED DATA:嘗試獲取更新鎖 (U 鎖 → X 鎖)
- 若目標列已被其他交易鎖定 (X 鎖) → 直接跳過,不等待,返回 SQLCODE=100
- 若目標列未被鎖定 → 成功獲取鎖,返回資料列
- 完美解決「非阻塞檢查」需求
-- 允許最多 2 個應用同時存取 INDEX_1
INSERT INTO LockTable VALUES('INDEX_1');
INSERT INTO LockTable VALUES('INDEX_1');
-- 允許最多 3 個應用同時存取 INDEX_2
INSERT INTO LockTable VALUES('INDEX_2');
INSERT INTO LockTable VALUES('INDEX_2');
INSERT INTO LockTable VALUES('INDEX_2');
每次 lock() 嘗試獲取任一可用列,列數即為最大並發數。
五、完整流程時序圖
應用 A 應用 B LockTable
| | |
|--- lock('INDEX_1') ---->| |
| (子執行緒 SELECT | |
| FOR UPDATE SKIP | |
| LOCKED DATA) | |
|<-- 成功獲取列 1 鎖 -----| |
| | |
| 業務處理... | |
| (主執行緒可自由 | |
| COMMIT/ROLLBACK) | |
| |--- lock('INDEX_1') ---->|
| | (子執行嘗試獲取列 2)|
| |<-- 成功獲取列 2 鎖 -----|
| | |
| | 業務處理... |
| | |
|--- unlock() ----------->| |
| (子執行緒 CLOSE/ | |
| ROLLBACK 釋放列 1) | |
| | |
| |--- unlock() ----------->|
| | (釋放列 2) |
六、擴展應用:資源池並發控制
-- 資源池:10 個資料庫連線授權
-- LockTable 插入 10 列 'DB_CONN_LICENSE'
-- 應用啟動時 lock('DB_CONN_LICENSE'),關閉時 unlock()
-- 自動實現連線池大小限制,無需外部協調器
七、遷移與相容性提醒
- 僅支援 DB2 for z/OS V9.1+,LUW 系列需不同實現
- 舊版語法
NEXTVAL/PREVVAL 仍支援但不建議新系統使用
- 高可用環境 (Data Sharing) 需確保鎖表在所有成員可見
結語
利用 SKIP LOCKED DATA 實現會話鎖定,是「以鎖治鎖」的經典設計模式:用資料庫內建的非阻塞鎖獲取機制,構建應用層級的邏輯互斥原語。這種模式簡單、可靠、無外部依賴,已在多個核心 DB2 z/OS 專案中驗證成功。
- 上一篇文章: DB2 Cube Views 元數據橋接建構指南:OLAP 整合與多維建模實戰
- 下一篇文章: DB2 資料庫備份實戰範例大全:離線、線上、增量、磁帶與 TSM 多場景指令集
Copyright ©
程式碼之家 All Rights Reserved