Mysql進行操作時鎖的具體行為


場景一:單個事務更新一條存在的數據

假設有表 user (id PK, name, age),數據:[id=1, name='Alice', age=25]

你的 SQL: UPDATE user SET age = 26 WHERE id = 1;

底層動作:

  1. 事務 A (主動方) 發起更新請求。
  2. Lock Manager 介入:
    • 查找 id=1 的索引記錄: Lock Manager 根據 id=1(主鍵)找到對應的主鍵索引樹葉子節點中的那條索引記錄
    • 檢查這條索引記錄上的鎖狀態: 發現 id=1 這條索引記錄此刻是無鎖狀態
    • 在索引記錄上“粘貼”一個鎖標記: Lock Manager 會在 id=1 這條具體的索引記錄上,打上一個**“X 鎖 (Exclusive Lock)”**的標記。
      • 這個標記就是一條內部的內存數據結構,記錄著:“id=1 這條索引記錄,現在被事務 AX 模式鎖住,并且引用計數+1”。
    • 在對應的表頭部“粘貼”一個意向鎖標記: 同時,Lock Manager 還會順手在 user 表的內部元數據結構上,打上一個**“IX 鎖 (Intention Exclusive Lock)”**的標記。
      • 這個標記是:“user 表的某個地方,有事務正在嘗試或已經持有 X 型行鎖。”
  3. 事務 A 執行更新: 事務 A 獲得鎖,可以安全地修改 id=1 這條索引記錄的 age 值。
  4. 事務 A 提交/回滾: 事務 A 結束時,Lock Manager 會根據之前的記錄,移除 id=1 上的 X 鎖標記,同時檢查 user 表是否還有其他 IX 鎖持有者,如果沒有,也移除 user 表上的 IX 鎖標記。

場景二:事務 A 更新數據,事務 B 隨后讀取同一條數據

數據:[id=1, name='Alice', age=25]

你的 SQL (事務 A): UPDATE user SET name = 'Alicia' WHERE id = 1;
你的 SQL (事務 B): SELECT * FROM user WHERE id = 1;

底層動作:

  1. 事務 A 獲得 id=1X 鎖 (如場景一所述)。
    • id=1 索引記錄上:X 鎖,持有者 事務 A
    • user 表元數據上:IX 鎖,持有者 事務 A
  2. 事務 B 發起讀取請求。
  3. Lock Manager 介入:
    • 查找 id=1 的索引記錄。
    • 檢查這條索引記錄上的鎖狀態: 發現 id=1 這條索引記錄上有一個 X 鎖,并且持有者是**事務 A**。
    • 判斷沖突: 事務 B 嘗試讀取,但 事務 A 持有的是 X 鎖 (排他鎖)。X 鎖會阻止所有其他事務的讀寫。
    • 不授予鎖,并等待: Lock Manager 不授予事務 B 任何鎖,而是把事務 B 放入一個等待隊列,同時啟動事務 B 的等待計時器
      • “事務 B 正在等待 id=1 這條索引記錄上的鎖。”
  4. 事務 A 提交: 釋放 id=1 上的 X 鎖,也釋放 user 表的 IX 鎖。
  5. Lock Manager 通知: id=1 上的鎖被移除,Lock Manager 發現等待隊列中有事務 B。
  6. 事務 B 被喚醒: 事務 B 獲得執行權限,讀取 id=1 這條記錄的新數據(比如 name='Alicia')。

場景三:間隙鎖 (Gap Lock) 的具體行為 (防止幻讀)

數據:user (id PK),記錄只有 [id=10], [id=30] (沒有 id=20)

你的 SQL (事務 A): SELECT * FROM user WHERE id BETWEEN 15 AND 25 FOR UPDATE; (注意這是范圍查詢且帶 FOR UPDATE)

底層動作:

  1. 事務 A 發起請求。
  2. Lock Manager 介入:
    • 查找索引: Lock Manager 根據條件 id BETWEEN 15 AND 25,在主鍵索引樹上進行查找。
    • 發現沒有符合條件的記錄 (這是一個空區間/間隙)。
    • 在“間隙”上打鎖標記: 盡管沒有找到具體的數據行,Lock Manager 依然會在索引結構中,針對 id=10id=30 之間的**“范圍”(即 (10, 30) 這個間隙),打上一個“間隙鎖 (Gap Lock)”**的標記。
      • 這個標記就是:“索引中 id 值在 1030 之間的空地,現在被事務 A 鎖住,禁止插入新數據。”
      • 通常,這個間隙鎖是 X 類型的,因為它阻止其他事務在這個間隙中進行 INSERT 操作。
    • 在對應的表頭部“粘貼”一個意向鎖標記 (IX)
  3. 事務 B 嘗試插入數據: INSERT INTO user (id) VALUES (20);
  4. Lock Manager 介入:
    • 判斷插入位置: 發現 id=20 應該插入到 id=10id=30 之間。
    • 檢查該間隙的鎖狀態: 發現這個 (10, 30) 的間隙上有一個間隙鎖,持有者是**事務 A**。
    • 不授予鎖,并等待: Lock Manager 不授予事務 B 任何鎖,將事務 B 放入等待隊列
  5. 事務 A 提交: 釋放 (10, 30) 上的間隙鎖,以及 user 表的 IX 鎖。
  6. Lock Manager 通知: 間隙鎖被移除,事務 B 被喚醒,可以成功插入 id=20

這些“鎖標記”本質上都是數據庫系統內部維護的內存數據結構,它們記錄著:哪個事務在哪個資源(索引記錄或間隙或表)上持有哪種類型的鎖。當其他事務請求時,Lock Manager 就去查這些標記,進行兼容性判斷,決定是立即授予、等待還是死鎖。
內存中的鎖管理數據結構,它們并不是簡單的“標記”那么純粹,而是一系列精巧組織的對象。

要理解這個,我們得從 Lock Manager (鎖管理器) 的核心工作開始。Lock Manager 維護著一張“活的地圖”,這張地圖記錄了哪些資源被鎖了被誰鎖了鎖的類型是什么,以及誰在等待這些鎖


最底層數據結構模擬:Lock Manager 的“活地圖”

想象 Lock Manager 就好比一個大型交通控制中心,它有幾塊巨大的顯示屏和一些重要的記錄本。

核心數據結構 1:鎖哈希表 (Lock Hash Table) 或 鎖鏈表 (Lock List)

這是所有正在活動的鎖及其等待者的“索引”。

  • 目的快速查找某個資源(比如某行數據)上是否有鎖,以及有哪些事務在等待。
  • 實現:通常是一個哈希表std::unordered_map 類似),鍵是資源標識符,值是一個鏈表或隊列,里面包含了所有作用在該資源上的鎖對象和等待者。因為哈希表的查找速度快,能迅速定位到某個資源。

模擬其內部結構:

// 這是一個高度簡化的偽代碼,模擬內存中的核心結構// 1. 資源標識符 (Resource Identifier) - 鎖住哪個具體的“東西”
//    這是鎖的“粒度”所在,可以是一個Page ID + Index ID + Record ID,也可以是表ID
struct LockResource {enum ResourceType {TABLE_LOCK,    // 表級RECORD_LOCK,   // 行級GAP_LOCK       // 間隙};ResourceType type;long long tableId; // 表的唯一標識long long pageId;  // 數據頁的唯一標識 (行鎖和間隙鎖可能需要)long long indexId; // 索引的唯一標識 (行鎖和間隙鎖需要)// 對于Record Lock,可能還需要存儲記錄的在頁面內的具體位置或哈希值// 對于Gap Lock,可能需要存儲間隙的起始和結束點(如索引鍵值,或其他內部指針)std::string recordKeyHash; // 簡化表示:實際是索引鍵值的hash或物理位置// 確保 LockResource 可以作為哈希表的鍵bool operator==(const LockResource& other) const { /* 比較所有成員 */ }size_t operator()(const LockResource& res) const { /* 計算哈希值 */ }
};// 2. 具體的鎖對象 (Lock Object) - 鎖本身的信息
struct LockObject {enum LockMode {IS_LOCK,   // Intention Shared (表級意向共享)IX_LOCK,   // Intention Exclusive (表級意向排他)S_LOCK,    // Shared (讀鎖,共享鎖)X_LOCK     // Exclusive (寫鎖,排他鎖)};LockMode mode;long long transactionId; // 持有這個鎖的事務IDint lockCount;           // 鎖計數 (用于可重入性), 比如 SELECT ...FOR UPDATE 兩次bool isWaiting;          // 這個事務是否正在等待這個鎖?// 指向下一個等待這個資源的鎖對象(如果存在的話)// 或者指向下一個被該事務持有的鎖對象LockObject* nextLockInResourceList; // 針對同一資源的所有鎖和等待者鏈表LockObject* nextLockInTxList;       // 某個事務持有的所有鎖鏈表
};// 3. 鎖哈希表 - 核心的數據結構
// Key: LockResource (哪個資源被鎖)
// Value: 一個鏈表/隊列,包含所有作用在該資源上的 LockObject
std::unordered_map<LockResource, std::list<LockObject>> globalLockHashTable;

理解 globalLockHashTable 里的“東西”:

  • 每個節點上
    • 沒有獨立的“鎖標記”。相反,數據庫管理著一個集中的 Lock Manager
    • 當你說的“節點”是時,表上會有意向鎖ISIX)的記錄,這些記錄也會被存放在 globalLockHashTable 中。LockResourcetype 會是 TABLE_LOCK
    • 當你說的“節點”是時,它指的就是索引記錄 (Index Record)。這才是 InnoDB 行級鎖的真正目標。LockResourcetype 會是 RECORD_LOCKGAP_LOCK
核心數據結構 2:事務持有的鎖列表 (Transaction’s Lock List)

除了按資源查找鎖,Lock Manager 還需要知道一個事務到底持有哪些鎖,以便在事務提交或回滾時能迅速釋放它們。

  • 目的快速釋放一個事務持有的所有鎖。
  • 實現:每個活躍事務內部,或者 Lock Manager 維護一個映射Transaction ID -> List of LockObject

模擬其內部結構:

// 4. Per-Transaction Lock List - 每個活躍事務會有一個這樣的內部列表
//   一個事務 A 內部可能有一個指針指向它所持有的第一個 LockObject
//   或者 Lock Manager 維護一個 map:
std::unordered_map<long long, std::list<LockObject*>> transactionLocksMap;
// 這個 list 里面的 LockObject* 都是上面 globalLockHashTable 里的指針

可視化模擬:

假設有表 user (id PK, name),數據:[id=1], [id=5], [id=10]

事務 A 操作:UPDATE user SET name='New' WHERE id=1;
事務 B 操作:SELECT * FROM user WHERE id BETWEEN 3 AND 7 FOR UPDATE;

Lock Manager 內部狀態(簡化):

{"globalLockHashTable": {// 資源1: 用戶表, TABLE_LOCK類型"Resource_Table_user": [{"mode": "IX_LOCK",          // 意向排他鎖"transactionId": "TxA","lockCount": 1,"isWaiting": false},{"mode": "IX_LOCK",          // 意向排他鎖 (TxB也會加IX)"transactionId": "TxB","lockCount": 1,"isWaiting": false}],// 資源2: id=1 的索引記錄, RECORD_LOCK類型"Resource_Record_user_id_1": [{"mode": "X_LOCK",           // 排他鎖"transactionId": "TxA","lockCount": 1,"isWaiting": false}],// 資源3: "(1,5)" 間隙(id=5前面),GAP_LOCK類型"Resource_Gap_user_(1,5)": [{"mode": "X_LOCK",           // 間隙鎖是排他的"transactionId": "TxB","lockCount": 1,"isWaiting": false}],// 資源4: "id=5" 記錄,RECORD_LOCK類型"Resource_Record_user_id_5": [{"mode": "X_LOCK",           // Next-key lock會包含記錄本身"transactionId": "TxB","lockCount": 1,"isWaiting": false}],// 資源5: "(5,10)" 間隙,GAP_LOCK類型"Resource_Gap_user_(5,10)": [{"mode": "X_LOCK",           // 間隙鎖是排他的"transactionId": "TxB","lockCount": 1,"isWaiting": false}]// ... 其他資源},"transactionLocksMap": {"TxA": ["Resource_Table_user[IX_LOCK_TxA]","Resource_Record_user_id_1[X_LOCK_TxA]"],"TxB": ["Resource_Table_user[IX_LOCK_TxB]","Resource_Gap_user_(1,5)[X_LOCK_TxB]","Resource_Record_user_id_5[X_LOCK_TxB]","Resource_Gap_user_(5,10)[X_LOCK_TxB]"]}
}
死鎖檢測器的“行為”:

死鎖檢測器會定期(或在每次等待發生時)遍歷 globalLockHashTable 中的等待鏈表,并結合 transactionLocksMap 來構建一個**“等待圖 (Waits-for Graph)”**。

等待圖:

  • 節點:事務 ID (TxA, TxB)。
  • 邊:如果 TxA 在等待 TxB 釋放某個鎖,則從 TxA 指向 TxB。

偽算法:

  1. “老鐵,數據庫里現在誰在等誰啊?”
  2. 遍歷 globalLockHashTable 里的每一個 LockObject
  3. 如果 LockObject.isWaitingtrue
    • 找出這個 LockObject 對應的 LockResource
    • 找出目前正在持有這個 LockResource 上的沖突鎖的那個 LockObjecttransactionId (假設是 TxC)。
    • 那么,LockObject.transactionId 正在等待 TxC
    • 在內存的**“等待圖”**中,就畫一條邊:LockObject.transactionId --> TxC
  4. “圖畫好了!現在我們看看有沒有循環
  5. 在“等待圖”中進行深度優先搜索 (DFS)拓撲排序等算法來檢測是否存在
    • 如果發現 TxA --> TxB --> TxC --> TxA 這樣的循環,警報!死鎖!
  6. “有了循環!挑選一個受害者,把它回滾,讓它釋放所有鎖,打破這個循環!”

“每個節點上每個表上都有鎖標記嗎?”

  • 不是每個表“節點”上都有獨立的鎖標記,而是統一由 Lock Manager 在內存中管理這些 LockObject 實例。
  • 表上:會有 IS/IX 意向鎖的 LockObject
  • 行上特指索引記錄 (Index Record) 上,會有 S/X 共享/排他鎖的 LockObject
  • 間隙上特指索引的空閑區域 (Gap) 上,會有 Gap LockLockObject

所有這些 LockObject 都被組織在 globalLockHashTable 中(按資源分類)以及 transactionLocksMap 中(按事務分類),供 Lock Manager 高效地查找、管理、沖突檢測和死鎖檢測。它們是實時變化的內存數據,支撐著數據庫的并發控制。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917875.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917875.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917875.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

人工智能領域、圖歐科技、IMYAI智能助手2025年7月更新月報

IMYAI 平臺 2025 年 7 月重要功能更新與優化匯總 2025年07月31日更新 細節優化&#xff1a; 修復了移動端提交后自動彈出側邊欄的BUG。優化對話高級配置界面&#xff0c;增加滾動條并固定高度&#xff0c;避免內容超出屏幕。音樂生成界面的人聲選擇新增“合唱”選項&#xff…

HTTP 與 HTTPS 的區別深度解析:從原理到實踐

HTTP 和 HTTPS 是現代 Web 開發中不可或缺的協議&#xff0c;它們決定了瀏覽器與服務器之間數據傳輸的方式。HTTPS 作為 HTTP 的安全版本&#xff0c;在安全性、性能和用戶體驗上都有顯著提升。本文將通過萬字篇幅&#xff0c;結合圖表和代碼示例&#xff0c;詳細剖析 HTTP 與 …

STM32F407VET6學習筆記11:smallmodbus_(多從機)創建新的slave從機

今日記錄一些smallmodbus 創建新的slave 從機 的過程&#xff0c;以及使用的關鍵點. 目錄 創建新的從機對應操作函數與buffer 創建新的從機線程與操作代碼&#xff1a; slave使用的要點&#xff1a; 完整的slave代碼&#xff1a; 能正常通信&#xff1a; 創建新的從機對應操作函…

【論文閱讀】Transformer Feed-Forward Layers Are Key-Value Memories

Transformer Feed-Forward Layers Are Key-Value Memories 原文摘要 研究背景與問題&#xff1a; 前饋層占Transformer模型參數總量的2/3&#xff0c;但其功能機制尚未得到充分研究 核心發現&#xff1a;提出前饋層實質上是鍵值存儲系統 鍵&#xff1a;這里的鍵與訓練數據中出…

昇思+昇騰開發板:DeepSeek-R1-Distill-Qwen-1.5B 模型推理部署與 JIT 優化實踐

目錄 引言 模型推理部署 環境準備 安裝 MindSpore 查看當前 mindspore 版本 安裝 MindNLP 模型與分詞器加載 導入必要的庫 加載分詞器 加載模型 對話功能實現 設置系統提示詞 構建對話歷史輸入 推理函數實現 交互界面實現 推理JIT優化 基礎環境安裝 JIT 優化配置…

用phpstudy安裝php8.2后報錯:意思是找不到php_redis.dll拓展時

1.地址&#xff1a;https://pecl.php.net/package/redis/6.2.0/windows 2.下載3.解壓后復制php_redis.dll到phpstudy_pro\Extensions\php\php8.2.9nts\ext目錄 4.打開php.ini&#xff0c;加上 extension_dir “D:\software\phpstudy_pro\Extensions\php\php8.2.9nts\ext”

開源列式分布式數據庫clickhouse

這里寫自定義目錄標題開源列式OLAP數據庫clickhouseclickhouse使用 ClickHouse 的場景如何理解行式存儲和列式存儲clickhouse-go開源列式OLAP數據庫clickhouse OLAP (分析型)&#xff1a;專為快速掃描、聚合、分析海量數據設計。OLTP (事務型)&#xff1a;專為處理大量短事務&…

Java Stream API 詳解(Java 8+)

1. Stream 操作分類Stream 操作分為兩類&#xff1a;中間操作&#xff08;Intermediate Operations&#xff09;返回新的 Stream&#xff0c;可以鏈式調用&#xff08;如 filter, map, sorted, distinct&#xff09;。惰性求值&#xff1a;只有遇到終止操作時才會執行。終止操作…

「源力覺醒 創作者計劃」_文心大模型4.5系列開源模型, 從一行代碼到一個生態:聊聊開源戰略那些事兒,順便扯扯文心大模型 4.5 的使用心得

前言&#xff1a;哈嘍&#xff0c;大家好&#xff0c;今天給大家分享一篇文章&#xff01;并提供具體代碼幫助大家深入理解&#xff0c;徹底掌握&#xff01;創作不易&#xff0c;如果能幫助到大家或者給大家一些靈感和啟發&#xff0c;歡迎收藏關注哦 &#x1f495; 目錄從一行…

算法專題(二)回文鏈表

1、源代碼class Solution {public boolean isPalindrome(ListNode head) {ListNode fasthead,slowhead; //快慢指針都在頭結點//快指針走2步&#xff0c;慢指針走一步。//雙數快指針最后是null&#xff0c;單數快指針下一位是nullwhile(fast!null && fast.next!null){f…

2025《艾諾提亞失落之歌》逆向工程解包嘗試

前言 想開發一下光明之魂&#xff0c;看能不能解包《艾諾提亞失落之歌》的模型。 之前寫了&#xff08;https://blog.csdn.net/weixin_42875245/article/details/148616547?spm1001.2014.3001.5501&#xff09; 沿用這個思路進行逆向工程解包。 文章目錄請添加圖片描述前言…

JVM 03 類加載機制

JVM 將字節碼二進制流加載到內存稱為類加載。 什么時候加載類 new 實例化對象。而對象所屬類還沒被加載。讀取/設置類的靜態非常量字段&#xff0c;常量字段在常量池。調用類的靜態方法。類初始化&#xff0c;優先初始化父類。虛擬機啟動時&#xff0c;先加載用戶指定的主類。 …

STM32H7+FreeRTOS+LwIP移植EtherCAT開源主站SOEM

代碼下載什么的就不多說了&#xff0c;直接看需要移植修改的代碼。 1、osal.c修改 /******************************************************************************* * *** **** *** *** …

VijosOJ:中文信息學競賽的二十年開源之路

VijosOJ&#xff1a;中文信息學競賽領域的老牌開源在線判題系統 在中文編程教育與信息學競賽的發展歷程中&#xff0c;在線判題系統&#xff08;OJ&#xff09;扮演了至關重要的角色。它們不僅是選手訓練的 “戰場”&#xff0c;更是知識傳遞與社區交流的樞紐。VijosOJ&#x…

QPainter::CompositionMode解析

基本概念目標(Destination)&#xff1a;已經存在的像素。源(Source)&#xff1a;要繪制的新像素。組合模式&#xff1a;決定源和目標如何混合。總結SourceOver&#xff1a;源繪制在目標之上。DestinationOver&#xff1a;目標繪制在源之上。Clear&#xff1a;二者重疊區域被清空…

對接釘釘審批過程記錄(C#版本)

釘釘開放平臺&#xff1a;API總覽 - 釘釘開放平臺 按照開放平臺操作指引&#xff0c;進入到釘釘開發者后臺&#xff1a;開發者后臺統一登錄 - 釘釘統一身份認證&#xff0c;進行應用創建。 按照開放平臺指引下載釘釘SDK&#xff08;新版&#xff09;。 在vs引入釘釘dll文件。 獲…

AFSIM入門教程03.03:更新所有依賴庫版本

系列索引&#xff1a;AFSIM入門教程索引 上一篇中更新了tiff庫版本&#xff0c;本文將更新所有使用到的依賴庫版本。 失敗了 依賴庫 首先獲取哪些庫被使用了。打開源碼目錄&#xff0c;搜索# Configure the 3rd_party&#xff0c;可以看到調用第三方庫的代碼。 官方提供的…

完美解決hive external表中csv字段內容含“,“逗號的問題

為解決hive表中csv字段內容含","逗號的問題&#xff0c;網上幾乎都是說要用org.apache.hadoop.hive.serde2.OpenCSVSerde。 使用方法為&#xff1a; 1、mysql導出時&#xff0c;加一個ENCLOSED BY ‘"’&#xff0c; 示例&#xff1a; mysql -h 10.16.0.10 -P …

【Git】修改本地和遠程的分支名稱

其原理是&#xff1a; 對于本地&#xff1a;可直接修改分支名稱&#xff1b;對于遠程&#xff1a;不可直接重命名分支&#xff0c;所以應該將修改好名稱的分支以新分支的形式推送上遠程倉庫&#xff0c;之后將新分支與遠程新分支關聯&#xff0c;之后可選擇刪除舊分支# 例子&am…

ubuntu24.04安裝selenium、chrome、chromedriver

實驗環境&#xff1a;kaggle notebook、colab notebook1、安裝chrome!wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb!sudo dpkg -i google-chrome-stable_current_amd64.deb!sudo apt-get install -f!export QT_QPA_PLATFORMoffscreen!sudo…