最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

在MySQL中不建議使用長事務的根因詳析

 更新時間:2025年12月23日 09:59:15   作者:Sam_Deep_Thinking  
長事務顧名思義就是運行時間比較長,長時間未提交的事務,也可以稱之為大事務,這類事務往往會造成大量的阻塞和鎖超時,容易造成主從延遲,要盡量避免使用長事務,這篇文章主要介紹了在MySQL中不建議使用長事務根因的相關資料,需要的朋友可以參考下

引言

“不要使用長事務”是 MySQL 開發(fā)與運維中的黃金準則。然而,許多開發(fā)者僅將其視為性能建議,卻未意識到其背后隱藏著系統(tǒng)級崩潰風險。本文將從 InnoDB 的底層機制出發(fā),結合具體事務 ID(trx_id)、Undo Log 版本鏈、Read View 快照等核心組件,徹底剖析:

  • 為什么 REPEATABLE READ 隔離級別必須維護歷史版本;
  • 為什么一個只包含 SELECT 的事務也能導致磁盤寫滿;
  • 為什么“事務中調(diào)用支付接口”這類看似合理的代碼會引發(fā)雪崩。

只有理解了 MVCC 的完整工作流,才能真正明白:長事務的本質(zhì),是讓整個數(shù)據(jù)庫為你的快照背負歷史包袱。

一、可重復讀(REPEATABLE READ)的實現(xiàn)原理

MySQL InnoDB 引擎在 REPEATABLE READ 隔離級別下,通過 MVCC(多版本并發(fā)控制) 實現(xiàn)一致性非鎖定讀。其核心依賴三個要素:

  1. 每行記錄的隱藏字段

    • DB_TRX_ID:最后一次修改該行的事務 ID;
    • DB_ROLL_PTR:指向 Undo Log 中的歷史版本指針。
  2. Undo Log:存儲數(shù)據(jù)的歷史版本,形成版本鏈(Version Chain)。

  3. Read View:事務執(zhí)行第一個 SELECT 時創(chuàng)建的一致性視圖,用于判斷哪些版本可見。

1.1 版本鏈示例

假設初始插入由事務 trx_id = 100 完成:

INSERT INTO accounts (id, balance) VALUES (1, 100);

隨后三次更新分別由 trx_id = 101, 102, 103 執(zhí)行:

版本balanceDB_TRX_IDUndo 指向
V4400103→ V3
V3300102→ V2
V2200101→ V1
V1100100NULL

物理上只保留最新版本 V4,其余通過 Undo Log 鏈式回溯。

1.2 Read View 是什么?——原理與機制

Read View(讀視圖)是 InnoDB 為實現(xiàn) MVCC 而在內(nèi)存中動態(tài)構建的一個一致性快照結構。它的核心作用是:在不加鎖的前提下,讓事務看到一個“邏輯上一致”的數(shù)據(jù)庫狀態(tài)

關鍵特性:

  • ? 純內(nèi)存結構:Read View 不寫入磁盤,不持久化,僅存在于事務執(zhí)行期間的內(nèi)存中。
  • ? 一次性創(chuàng)建:在 REPEATABLE READ 隔離級別下,事務執(zhí)行第一個 SELECT 語句時創(chuàng)建,之后全程復用,不再更新。
  • ? 事務私有:每個事務擁有自己的 Read View,彼此隔離。
  • ? 輕量但關鍵:雖然結構簡單,但它決定了整個事務能看到哪些數(shù)據(jù)版本。

為什么需要 Read View?

因為 InnoDB 的行記錄只保存最新版本,歷史版本在 Undo Log 中。當一個事務讀取數(shù)據(jù)時,它不能簡單地“看到最新值”——那樣會破壞隔離性。
Read View 提供了一套基于事務 ID 的可見性規(guī)則,讓事務能沿著 Undo 鏈找到“它應該看到的那個版本”。

Read View 的內(nèi)部字段

字段含義
m_ids創(chuàng)建 Read View 時,所有活躍(未提交)事務的 ID 列表。這些事務的修改對當前事務不可見。
m_up_limit_idm_ids 中的最小值。即 最小活躍事務 ID。小于該值的事務都已提交。
m_low_limit_idmax(m_ids) + 1。即 下一個將要分配的事務 ID。大于等于該值的事務在 Read View 創(chuàng)建時尚未開始,屬于“未來事務”。
m_creator_trx_id當前事務自身的 trx_id。用于識別“自己修改的數(shù)據(jù)”,即使未提交也可見。

?? 舉例說明
假設事務 T(trx_id=150)創(chuàng)建 Read View 時,系統(tǒng)中只有它自己活躍,則:

  • m_ids = [150]
  • m_up_limit_id = min([150]) = 150
  • m_low_limit_id = max([150]) + 1 = 151
  • m_creator_trx_id = 150

1.3 可見性判斷規(guī)則

基于上述字段,InnoDB 對某一行版本的 DB_TRX_ID 進行如下判斷:

  1. 如果是自己修改的
    DB_TRX_ID == m_creator_trx_id → ? 可見

  2. 如果是未來事務產(chǎn)生的
    DB_TRX_ID >= m_low_limit_id → ? 不可見

  3. 如果是過去已提交事務產(chǎn)生的
    DB_TRX_ID < m_up_limit_id → ? 可見。

  4. 如果是當時活躍但非自己的事務產(chǎn)生的
    DB_TRX_ID ∈ m_ids≠ m_creator_trx_id → ? 不可見

  5. 其他情況(如 DB_TRX_ID 在 [m_up_limit_id, m_low_limit_id) 區(qū)間但不在 m_ids 中)
    表示該事務在 Read View 創(chuàng)建前已提交 → ? 可見。

  6. 若當前版本不可見,則沿 Undo 鏈向上查找,直到找到可見版本或鏈尾。

?? 關鍵點:Read View 一旦創(chuàng)建,在 REPEATABLE READ 下全程復用,直到事務結束

二、案例一:只讀事務導致 Undo 日志無法清理

2.1 場景還原

事務 T1(trx_id = 150) 執(zhí)行以下操作后忘記提交:

-- T=0
START TRANSACTION;
SELECT balance FROM accounts WHERE id = 1;  -- ← 創(chuàng)建 Read View
-- 事務掛起 6 小時

根據(jù)上述規(guī)則,其 Read View 為:

  • m_ids = [150]
  • m_up_limit_id = 150
  • m_low_limit_id = 151
  • m_creator_trx_id = 150

這意味著:所有 DB_TRX_ID < 151 的版本都必須保留,因為它們可能被 T1 讀取。

2.2 并發(fā)更新與 Undo 積壓

與此同時,業(yè)務系統(tǒng)高頻更新同一行(如用戶積分),每秒一次,由連續(xù)遞增的事務執(zhí)行:

-- trx_id = 151, 152, 153, ..., 21750(6小時共21600次)
UPDATE accounts SET points = points + 1 WHERE id = 1;

每次 UPDATE 生成新版本和 Undo 記錄。

為什么不能清理?

  • Purge 線程清理條件:所有活躍事務都不再需要該舊版本。
  • 事務 150 的 Read View 要求:所有 DB_TRX_ID < 151 的版本必須保留(包括最初的 trx_id=100)。
  • Undo 是鏈式結構,只要最老版本(V1)不能刪,整條鏈都必須保留
  • 因此,即使 trx_id=151~21750 的事務早已提交,它們的 Undo 仍因依賴 V1 而無法 purge。

2.3 故障后果

  • Undo 表空間從 500MB 膨脹至 8GB+;
  • ibdata1 文件寫滿,數(shù)據(jù)庫進入只讀模式;
  • 監(jiān)控指標:
    • History list length > 200,000;
    • 簡單查詢延遲從 0.3ms 升至 50ms;
    • 磁盤 IO util 達 98%。

?? 結論:即使沒有 DML,一個未提交的 SELECT 也能拖垮整個數(shù)據(jù)庫。

三、案例二:應用層“合理”長事務引發(fā)雪崩

3.1 典型下單流程代碼

@Transactional
public void placeOrder(Long userId, Long productId) {
    // 1. 查庫存(SELECT)
    int stock = productMapper.selectStock(productId); // ← 創(chuàng)建 Read View!

    // 2. 調(diào)用第三方支付(網(wǎng)絡 I/O,耗時 10~30 秒)
    paymentService.callRemoteAPI(...); // ?? 事務掛起!

    // 3. 扣庫存 + 保存訂單
    productMapper.decreaseStock(productId);
    orderMapper.insert(new Order(...));
}

假設該事務分配到 trx_id = 22000。

  • Read View:
    • m_ids = [22000]
    • m_up_limit_id = 22000
    • m_low_limit_id = 22001
    • m_creator_trx_id = 22000

這意味著:所有 DB_TRX_ID < 22001 的版本都必須保留。

3.2 高頻輔助更新放大危害

系統(tǒng)另有服務每秒更新商品瀏覽量 200 次,由 trx_id = 22001, 22002, … 執(zhí)行:

UPDATE products SET view_count = view_count + 1 WHERE id = 123;

在 20 秒內(nèi):

  • 產(chǎn)生 4,000 條 Undo 記錄(trx_id 22001 ~ 26000);
  • 所有記錄因事務 22000 的 Read View 而無法 purge(因為它們依賴更早版本)。

若同時有 50 個用戶下單:

  • Undo 增長速率 = 200 × 50 × 20 = 200,000 條/分鐘;
  • Purge backlog 暴漲;
  • 主從復制延遲從 1 秒升至 15 分鐘;
  • 應用超時率飆升。

3.3 根本原因

  • 問題不在新事務 ID 大,而在舊版本無法釋放;
  • Read View 凍結了歷史視角,迫使 InnoDB 保留從 trx_id=100 到當前的所有中間狀態(tài);
  • Undo 日志增長速度 = 熱點行更新頻率 × 長事務數(shù)量 × 持續(xù)時間。

四、長事務的四大系統(tǒng)級危害

危害類型機制后果
磁盤耗盡Undo 表空間無法 purgeibdata1 或 undo tablespace 寫滿,數(shù)據(jù)庫只讀/宕機
查詢性能暴跌版本鏈過長,MVCC 回溯成本高簡單 SELECT 延遲從 ms 級升至百 ms 級
主從延遲Binlog 積壓 + Slave 回放慢從庫數(shù)據(jù)嚴重滯后,讀寫分離失效
鎖沖突加劇行鎖持有時間過長其他會話阻塞,死鎖概率上升

結語

長事務的危害,源于 REPEATABLE READ 隔離級別下 Read View 與 Undo Log 的強耦合。
一個未提交的事務,就像一個“時間錨點”,將數(shù)據(jù)庫的歷史牢牢釘住,阻止系統(tǒng)輕裝前行。

真正的穩(wěn)定性,來自于對事務邊界的敬畏:

讓事務只做數(shù)據(jù)庫該做的事,且越快越好。

唯有如此,Undo 日志才能及時回收,版本鏈才不會無限延長,數(shù)據(jù)庫才能在高并發(fā)下穩(wěn)健運行。

到此這篇關于在MySQL中不建議使用長事務根因的文章就介紹到這了,更多相關MySQL不建議使用長事務內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • 在Ubuntu上檢查MySQL是否啟動并放開3306端口的常見方法

    在Ubuntu上檢查MySQL是否啟動并放開3306端口的常見方法

    在使用Ubuntu系統(tǒng)時,MySQL數(shù)據(jù)庫是許多開發(fā)人員和系統(tǒng)管理員的常用工具,本文將詳細介紹如何在Ubuntu上檢查MySQL是否啟動,以及如何放開MySQL默認的3306端口,以便允許外部訪問,需要的朋友可以參考下
    2025-07-07
  • 分享MYSQL插入數(shù)據(jù)時忽略重復數(shù)據(jù)的方法

    分享MYSQL插入數(shù)據(jù)時忽略重復數(shù)據(jù)的方法

    當程序中insert時,已存在的數(shù)據(jù)不插入,不存在的數(shù)據(jù)insert。在網(wǎng)上搜了下,可以使用存儲過程或者是用NOT EXISTS 來判斷是否存在
    2013-09-09
  • Mysql?DATETIME?毫秒坑的解決

    Mysql?DATETIME?毫秒坑的解決

    本文主要介紹了Mysql?DATETIME?毫秒坑的解決,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-01-01
  • MySQL學習筆記2:數(shù)據(jù)庫的基本操作(創(chuàng)建刪除查看)

    MySQL學習筆記2:數(shù)據(jù)庫的基本操作(創(chuàng)建刪除查看)

    我們所安裝的MySQL說白了是一個數(shù)據(jù)庫的管理工具,真正有價值的東西在于數(shù)據(jù)關系型數(shù)據(jù)庫的數(shù)據(jù)是以表的形式存在的,N個表匯總在一起就成了一個數(shù)據(jù)庫現(xiàn)在來看看數(shù)據(jù)庫的基本操作
    2013-01-01
  • MySQL約束(創(chuàng)建表時的各種條件說明)

    MySQL約束(創(chuàng)建表時的各種條件說明)

    這篇文章主要介紹了MySQL約束(創(chuàng)建表時的各種條件說明),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-06-06
  • 淺談MySQL模糊查詢中通配符的轉(zhuǎn)義

    淺談MySQL模糊查詢中通配符的轉(zhuǎn)義

    下面小編就為大家?guī)硪黄獪\談MySQL模糊查詢中通配符的轉(zhuǎn)義。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2016-12-12
  • 詳解用SELECT命令在MySQL執(zhí)行查詢操作的教程

    詳解用SELECT命令在MySQL執(zhí)行查詢操作的教程

    這篇文章主要介紹了詳解用SELECT命令在MySQL執(zhí)行查詢操作的教程,本文中還給出了基于PHP腳本的操作演示,需要的朋友可以參考下
    2015-05-05
  • 安裝mysq 5.7.20 解壓版遇到的坑(推薦)

    安裝mysq 5.7.20 解壓版遇到的坑(推薦)

    最近有朋友說當mysql5.7.20解壓版環(huán)境變量配置好后,根目錄沒有my.ini 也沒有 my-default.ini文件,怎么處理這個問題呢,下面小編給大家?guī)砹私鉀Q方案,大家可以參考下
    2017-11-11
  • mysql查詢上下級機構的方法實例

    mysql查詢上下級機構的方法實例

    大家應該都知道表里有上下級機構的,下面這篇文章主要給大家介紹了關于mysql查詢上下級機構的相關資料,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下
    2022-04-04
  • mysql:Can''t start server: can''t create PID file: No space left on device

    mysql:Can''t start server: can''t create PID file: No space

    這篇文章主要介紹了mysql啟動失敗不能正常啟動并報錯Can't start server: can't create PID file: No space left on device問題解決方法,需要的朋友可以參考下
    2015-05-05

最新評論

镇平县| 札达县| 富锦市| 呼图壁县| 新绛县| 榆树市| 昆山市| 左云县| 木里| 昌宁县| 册亨县| 南阳市| 香港| 衡东县| 通辽市| 喜德县| 本溪市| 临高县| 阿勒泰市| 麻江县| 鄄城县| 黔江区| 温泉县| 宿松县| 恩平市| 阿拉尔市| 申扎县| 桓仁| 大港区| 沙田区| 类乌齐县| 华坪县| 大化| 太谷县| 交城县| 古田县| 留坝县| 泰宁县| 墨玉县| 光山县| 莫力|