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

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

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

引言

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

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

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

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

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

  1. 每行記錄的隱藏字段

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

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

1.1 版本鏈?zhǔn)纠?/h3>

假設(shè)初始插入由事務(wù) 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 鏈?zhǔn)交厮荨?/p>

1.2 Read View 是什么?——原理與機(jī)制

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

關(guān)鍵特性:

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

為什么需要 Read View?

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

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

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

?? 舉例說明
假設(shè)事務(wù) T(trx_id=150)創(chuàng)建 Read View 時(shí),系統(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 對(duì)某一行版本的 DB_TRX_ID 進(jìn)行如下判斷:

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

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

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

  4. 如果是當(dāng)時(shí)活躍但非自己的事務(wù)產(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 中)
    表示該事務(wù)在 Read View 創(chuàng)建前已提交 → ? 可見。

  6. 若當(dāng)前版本不可見,則沿 Undo 鏈向上查找,直到找到可見版本或鏈尾

?? 關(guān)鍵點(diǎn):Read View 一旦創(chuàng)建,在 REPEATABLE READ 下全程復(fù)用,直到事務(wù)結(jié)束。

二、案例一:只讀事務(wù)導(dǎo)致 Undo 日志無法清理

2.1 場景還原

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

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

根據(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 的版本都必須保留,因?yàn)樗鼈兛赡鼙?T1 讀取。

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

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

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

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

為什么不能清理?

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

2.3 故障后果

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

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

三、案例二:應(yīng)用層“合理”長事務(wù)引發(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)絡(luò) I/O,耗時(shí) 10~30 秒)
    paymentService.callRemoteAPI(...); // ?? 事務(wù)掛起!

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

假設(shè)該事務(wù)分配到 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)另有服務(wù)每秒更新商品瀏覽量 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);
  • 所有記錄因事務(wù) 22000 的 Read View 而無法 purge(因?yàn)樗鼈円蕾嚫绨姹荆?/li>

若同時(shí)有 50 個(gè)用戶下單:

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

3.3 根本原因

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

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

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

結(jié)語

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

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

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

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

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

相關(guān)文章

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

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

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

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

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

    Mysql?DATETIME?毫秒坑的解決

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

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

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

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

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

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

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

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

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

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

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

    mysql查詢上下級(jí)機(jī)構(gòu)的方法實(shí)例

    大家應(yīng)該都知道表里有上下級(jí)機(jī)構(gòu)的,下面這篇文章主要給大家介紹了關(guān)于mysql查詢上下級(jí)機(jī)構(gòu)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    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啟動(dòng)失敗不能正常啟動(dòng)并報(bào)錯(cuò)Can't start server: can't create PID file: No space left on device問題解決方法,需要的朋友可以參考下
    2015-05-05

最新評(píng)論

茌平县| 安义县| 乌拉特后旗| 青海省| 曲水县| 年辖:市辖区| 两当县| 墨玉县| 孟连| 河津市| 盱眙县| 华蓥市| 潞城市| 怀来县| 昭苏县| 新乐市| 嘉荫县| 赞皇县| 巴南区| SHOW| 哈密市| 卓资县| 香格里拉县| 洛扎县| 吉隆县| 慈利县| 平和县| 南雄市| 青浦区| 谢通门县| 镇江市| 乌什县| 晋江市| 洪泽县| 丁青县| 和龙市| 钟祥市| 南木林县| 宁安市| 高淳县| 汤原县|