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

MySQL 事務(wù)隔離級(jí)別及實(shí)際業(yè)務(wù)應(yīng)用場(chǎng)景

 更新時(shí)間:2026年01月07日 09:11:12   作者:蕭曵 丶  
本文詳細(xì)介紹了MySQL的四種事務(wù)隔離級(jí)別(READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE),并探討了每種隔離級(jí)別在實(shí)際業(yè)務(wù)中的應(yīng)用場(chǎng)景,感興趣的朋友跟隨小編一起看看吧

一、事務(wù)隔離級(jí)別基礎(chǔ)

1.1 四種隔離級(jí)別概述

-- MySQL事務(wù)隔離級(jí)別(從低到高)
-- 1. READ UNCOMMITTED(讀未提交)
-- 2. READ COMMITTED(讀已提交)
-- 3. REPEATABLE READ(可重復(fù)讀)-- MySQL默認(rèn)級(jí)別
-- 4. SERIALIZABLE(串行化)

1.2 并發(fā)問(wèn)題類型

/**
 * 并發(fā)問(wèn)題分類:
 * 1. 臟讀(Dirty Read):讀取到其他事務(wù)未提交的數(shù)據(jù)
 * 2. 不可重復(fù)讀(Non-Repeatable Read):同一事務(wù)內(nèi)兩次讀取結(jié)果不一致
 * 3. 幻讀(Phantom Read):同一事務(wù)內(nèi)兩次查詢結(jié)果集不一致
 * 4. 丟失更新(Lost Update):兩個(gè)事務(wù)更新同一數(shù)據(jù),后提交的覆蓋了先提交的
 */

二、各隔離級(jí)別詳解

2.1 READ UNCOMMITTED(讀未提交)

-- 設(shè)置隔離級(jí)別為讀未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
-- 示例:臟讀問(wèn)題演示
-- 事務(wù)A
START TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE user_id = 1;
-- 此時(shí) balance 已修改但未提交
-- 事務(wù)B(在另一個(gè)連接)
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
START TRANSACTION;
SELECT balance FROM account WHERE user_id = 1; 
-- 會(huì)讀取到事務(wù)A未提交的修改(臟讀)
-- 如果事務(wù)A回滾,事務(wù)B讀取的數(shù)據(jù)就是錯(cuò)誤的

實(shí)際業(yè)務(wù)場(chǎng)景:

  • 幾乎不使用,除非對(duì)數(shù)據(jù)一致性要求極低
  • 可能的用途:實(shí)時(shí)監(jiān)控系統(tǒng),允許數(shù)據(jù)短暫不一致

2.2 READ COMMITTED(讀已提交)

-- 設(shè)置隔離級(jí)別為讀已提交
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
-- 示例:不可重復(fù)讀問(wèn)題演示
-- 事務(wù)A
START TRANSACTION;
SELECT balance FROM account WHERE user_id = 1;
-- 第一次讀?。篵alance = 1000
-- 事務(wù)B
START TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE user_id = 1;
COMMIT; -- 提交修改
-- 事務(wù)A再次讀取
SELECT balance FROM account WHERE user_id = 1;
-- 第二次讀?。篵alance = 900(不可重復(fù)讀)
COMMIT;

MVCC(多版本并發(fā)控制)實(shí)現(xiàn)原理:

-- MySQL在READ COMMITTED下的實(shí)現(xiàn)
-- 每行數(shù)據(jù)有隱藏字段:
-- DB_TRX_ID:最后修改該記錄的事務(wù)ID
-- DB_ROLL_PTR:回滾指針,指向undo log中的舊版本
-- DB_ROW_ID:行ID(隱藏主鍵)
-- 事務(wù)可見(jiàn)性規(guī)則:
-- 1. 版本號(hào)小于當(dāng)前事務(wù)ID的記錄
-- 2. 刪除版本號(hào)未定義或大于當(dāng)前事務(wù)ID
-- 3. 在READ COMMITTED下,每次查詢都重新生成ReadView

實(shí)際業(yè)務(wù)場(chǎng)景:

  • Oracle、PostgreSQL的默認(rèn)級(jí)別
  • 適合大多數(shù)OLTP系統(tǒng)
  • 報(bào)表系統(tǒng)(需要實(shí)時(shí)最新數(shù)據(jù))
  • 對(duì)數(shù)據(jù)實(shí)時(shí)性要求高的場(chǎng)景

2.3 REPEATABLE READ(可重復(fù)讀)- MySQL默認(rèn)

-- MySQL默認(rèn)隔離級(jí)別
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- 示例:解決不可重復(fù)讀
-- 事務(wù)A
START TRANSACTION;
SELECT balance FROM account WHERE user_id = 1;
-- 第一次讀取:balance = 1000
-- 事務(wù)B
START TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE user_id = 1;
COMMIT;
-- 事務(wù)A再次讀取
SELECT balance FROM account WHERE user_id = 1;
-- 第二次讀?。篵alance = 1000(可重復(fù)讀,讀取的是快照)
COMMIT;

幻讀問(wèn)題演示:

-- 事務(wù)A:統(tǒng)計(jì)賬戶數(shù)量
START TRANSACTION;
SELECT COUNT(*) FROM account WHERE balance > 0;
-- 返回:5
-- 事務(wù)B:插入新賬戶
START TRANSACTION;
INSERT INTO account(user_id, balance) VALUES (6, 500);
COMMIT;
-- 事務(wù)A:再次統(tǒng)計(jì)
SELECT COUNT(*) FROM account WHERE balance > 0;
-- 在REPEATABLE READ下,返回仍然是:5(沒(méi)有幻讀)
-- 但如果執(zhí)行UPDATE/INSERT,可能會(huì)看到"幻影行"

間隙鎖(Gap Lock)解決幻讀:

-- 事務(wù)A
START TRANSACTION;
-- 使用SELECT ... FOR UPDATE添加間隙鎖
SELECT * FROM account WHERE id > 100 FOR UPDATE;
-- 這會(huì)鎖定id>100的所有記錄和間隙
-- 事務(wù)B試圖插入
INSERT INTO account(id, user_id) VALUES (101, 6);
-- 會(huì)被阻塞,直到事務(wù)A提交
-- 查看當(dāng)前鎖信息
SHOW ENGINE INNODB STATUS;

實(shí)際業(yè)務(wù)場(chǎng)景:

  • 財(cái)務(wù)系統(tǒng)(需要事務(wù)內(nèi)數(shù)據(jù)一致性)
  • 對(duì)賬系統(tǒng)(統(tǒng)計(jì)期間數(shù)據(jù)不能變化)
  • 需要穩(wěn)定數(shù)據(jù)視圖的應(yīng)用

2.4 SERIALIZABLE(串行化)

-- 串行化隔離級(jí)別
SET SESSION TRANSACTION ISOLATION LEVEL SERIALIZABLE;
-- 所有SELECT語(yǔ)句都會(huì)隱式添加LOCK IN SHARE MODE
-- 事務(wù)A
START TRANSACTION;
SELECT balance FROM account WHERE user_id = 1;
-- 自動(dòng)加共享鎖
-- 事務(wù)B
START TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE user_id = 1;
-- 會(huì)被阻塞,直到事務(wù)A提交

實(shí)際業(yè)務(wù)場(chǎng)景:

  • 銀行轉(zhuǎn)賬(需要絕對(duì)數(shù)據(jù)一致性)
  • 庫(kù)存扣減(超高并發(fā)時(shí)需要串行化)
  • 敏感數(shù)據(jù)操作(如密碼重置)

三、MVCC(多版本并發(fā)控制)深度解析

3.1 MVCC實(shí)現(xiàn)原理

-- InnoDB MVCC數(shù)據(jù)結(jié)構(gòu)示例
CREATE TABLE account (
    id INT PRIMARY KEY,
    user_id INT,
    balance DECIMAL(10,2),
    -- 隱藏字段
    -- DB_TRX_ID: 6字節(jié),最后修改事務(wù)ID
    -- DB_ROLL_PTR: 7字節(jié),回滾指針
    -- DB_ROW_ID: 6字節(jié),隱藏主鍵
    -- DELETE BIT: 刪除標(biāo)記
);
-- ReadView創(chuàng)建時(shí)機(jī):
-- READ COMMITTED: 每次SELECT都創(chuàng)建新的ReadView
-- REPEATABLE READ: 第一次SELECT時(shí)創(chuàng)建ReadView,整個(gè)事務(wù)復(fù)用
-- 可見(jiàn)性判斷算法:
-- 1. 如果DB_TRX_ID < up_limit_id,可見(jiàn)(事務(wù)開(kāi)始前已提交)
-- 2. 如果DB_TRX_ID >= low_limit_id,不可見(jiàn)(事務(wù)開(kāi)始后開(kāi)始的)
-- 3. 如果DB_TRX_ID在活躍事務(wù)列表中,不可見(jiàn)(未提交)
-- 4. 否則可見(jiàn)

3.2 Undo Log鏈?zhǔn)纠?/h3>
-- 假設(shè)原始數(shù)據(jù)
-- id=1, balance=1000, DB_TRX_ID=10
-- 事務(wù)20修改
UPDATE account SET balance = 900 WHERE id = 1;
-- 新版本:balance=900, DB_TRX_ID=20, 回滾指針指向舊版本
-- 事務(wù)30修改
UPDATE account SET balance = 800 WHERE id = 1;
-- 新版本:balance=800, DB_TRX_ID=30, 回滾指針指向事務(wù)20的版本
-- 版本鏈:
-- 當(dāng)前版本(事務(wù)30) ← 事務(wù)20版本 ← 事務(wù)10版本

四、實(shí)際業(yè)務(wù)問(wèn)題與解決方案

4.1 電商庫(kù)存超賣問(wèn)題

-- 問(wèn)題場(chǎng)景:高并發(fā)下庫(kù)存扣減
-- 錯(cuò)誤做法(存在超賣風(fēng)險(xiǎn))
START TRANSACTION;
SELECT stock FROM product WHERE id = 1;
-- 假設(shè)stock = 10
IF stock > 0 THEN
    UPDATE product SET stock = stock - 1 WHERE id = 1;
END IF;
COMMIT;
-- 解決方案1:使用SELECT ... FOR UPDATE(悲觀鎖)
START TRANSACTION;
-- 加行鎖,阻止其他事務(wù)讀取
SELECT stock FROM product WHERE id = 1 FOR UPDATE;
-- 此時(shí)其他事務(wù)的SELECT ... FOR UPDATE會(huì)被阻塞
IF stock > 0 THEN
    UPDATE product SET stock = stock - 1 WHERE id = 1;
END IF;
COMMIT;
-- 解決方案2:使用樂(lè)觀鎖(版本控制)
ALTER TABLE product ADD version INT DEFAULT 0;
START TRANSACTION;
SELECT stock, version FROM product WHERE id = 1;
-- 假設(shè)stock=10, version=1
IF stock > 0 THEN
    UPDATE product SET 
        stock = stock - 1,
        version = version + 1 
    WHERE id = 1 AND version = 1;
    -- 如果影響行數(shù)為0,說(shuō)明版本已變,需要重試
END IF;
COMMIT;
-- 解決方案3:直接UPDATE判斷
START TRANSACTION;
UPDATE product SET stock = stock - 1 
WHERE id = 1 AND stock > 0;
-- 返回影響行數(shù),如果為0表示庫(kù)存不足
COMMIT;

4.2 銀行轉(zhuǎn)賬并發(fā)問(wèn)題

-- 場(chǎng)景:A向B轉(zhuǎn)賬,需要保證原子性和一致性
-- 問(wèn)題:并發(fā)轉(zhuǎn)賬可能導(dǎo)致余額錯(cuò)誤
-- 解決方案:使用SERIALIZABLE隔離級(jí)別或精心設(shè)計(jì)的事務(wù)
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
-- 關(guān)鍵:按固定順序加鎖,避免死鎖
-- 總是先鎖id小的賬戶
SELECT * FROM account 
WHERE id IN (1, 2) 
ORDER BY id 
FOR UPDATE;
-- 檢查A賬戶余額
SELECT balance FROM account WHERE id = 1;
-- 執(zhí)行轉(zhuǎn)賬
UPDATE account SET balance = balance - 100 WHERE id = 1;
UPDATE account SET balance = balance + 100 WHERE id = 2;
-- 記錄流水
INSERT INTO transfer_log(from_id, to_id, amount) VALUES (1, 2, 100);
COMMIT;

4.3 報(bào)表統(tǒng)計(jì)不一致問(wèn)題

-- 場(chǎng)景:生成財(cái)務(wù)報(bào)表時(shí),數(shù)據(jù)被其他事務(wù)修改
-- 要求:統(tǒng)計(jì)期間數(shù)據(jù)必須一致
-- 解決方案1:使用REPEATABLE READ + 開(kāi)始時(shí)間點(diǎn)
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
-- 記錄開(kāi)始時(shí)間
SET @report_time = NOW();
-- 統(tǒng)計(jì)邏輯(所有查詢看到的是同一時(shí)間點(diǎn)的快照)
SELECT SUM(balance) FROM account;
SELECT COUNT(*) FROM transfer_log WHERE create_time < @report_time;
COMMIT;
-- 解決方案2:使用備份或從庫(kù)查詢
-- 在從庫(kù)上使用REPEATABLE READ,不影響主庫(kù)性能
START TRANSACTION;
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
-- ... 統(tǒng)計(jì)查詢
COMMIT;

4.4 消息隊(duì)列消費(fèi)冪等性問(wèn)題

-- 場(chǎng)景:消息重復(fù)消費(fèi),需要保證冪等性
-- 問(wèn)題:重復(fù)處理同一消息
CREATE TABLE message_consumed (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    message_id VARCHAR(64) UNIQUE,  -- 消息唯一ID
    status TINYINT DEFAULT 0,
    consume_time DATETIME
);
-- 消費(fèi)消息時(shí)的冪等處理
START TRANSACTION;
-- 方案1:先插入,利用唯一索引保證冪等
INSERT IGNORE INTO message_consumed(message_id, consume_time) 
VALUES ('msg_123', NOW());
-- 如果插入成功(影響行數(shù)>0),則處理消息
IF ROW_COUNT() > 0 THEN
    -- 執(zhí)行業(yè)務(wù)邏輯
    CALL process_business_logic('msg_123');
    UPDATE message_consumed 
    SET status = 1 
    WHERE message_id = 'msg_123';
END IF;
COMMIT;

五、死鎖分析與解決

5.1 死鎖場(chǎng)景模擬

-- 事務(wù)A
START TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE id = 1;
-- 持有id=1的鎖
-- 事務(wù)B(同時(shí)執(zhí)行)
START TRANSACTION;
UPDATE account SET balance = balance - 200 WHERE id = 2;
-- 持有id=2的鎖
-- 事務(wù)A繼續(xù)
UPDATE account SET balance = balance + 100 WHERE id = 2;
-- 等待事務(wù)B釋放id=2的鎖
-- 事務(wù)B繼續(xù)
UPDATE account SET balance = balance + 200 WHERE id = 1;
-- 等待事務(wù)A釋放id=1的鎖
-- 死鎖發(fā)生!

5.2 死鎖檢測(cè)與解決

-- 查看死鎖日志
SHOW ENGINE INNODB STATUS;
-- 死鎖日志示例:
-- LATEST DETECTED DEADLOCK
-- *** (1) TRANSACTION:
-- TRANSACTION 3100, ACTIVE 2 sec starting index read
-- mysql tables in use 1, locked 1
-- LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s)
-- MySQL thread id 10, OS thread handle 1234, query id 100 updating
-- UPDATE account SET balance = balance + 100 WHERE id = 2
-- 預(yù)防死鎖策略:
-- 1. 按相同順序訪問(wèn)資源
-- 2. 減少事務(wù)執(zhí)行時(shí)間
-- 3. 使用低隔離級(jí)別(READ COMMITTED)
-- 4. 添加合理的索引,減少鎖范圍
-- 代碼層面的解決方案
START TRANSACTION;
-- 總是按id順序加鎖
SELECT * FROM account 
WHERE id IN (1, 2) 
ORDER BY id 
FOR UPDATE;
-- 執(zhí)行更新操作
UPDATE account SET balance = balance - 100 WHERE id = 1;
UPDATE account SET balance = balance + 100 WHERE id = 2;
COMMIT;

5.3 間隙鎖死鎖問(wèn)題

-- 間隙鎖導(dǎo)致的死鎖場(chǎng)景
-- 表結(jié)構(gòu)
CREATE TABLE orders (
    id INT PRIMARY KEY,
    user_id INT,
    amount DECIMAL(10,2),
    KEY idx_user_id (user_id)
);
-- 事務(wù)A
START TRANSACTION;
-- 對(duì)user_id=100加間隙鎖(鎖定100-200之間的間隙)
SELECT * FROM orders WHERE user_id = 150 FOR UPDATE;
-- 事務(wù)B
START TRANSACTION;
-- 對(duì)user_id=200加間隙鎖(鎖定100-200之間的間隙)
SELECT * FROM orders WHERE user_id = 180 FOR UPDATE;
-- 事務(wù)A嘗試插入
INSERT INTO orders(id, user_id) VALUES (1, 160);
-- 等待事務(wù)B的間隙鎖
-- 事務(wù)B嘗試插入
INSERT INTO orders(id, user_id) VALUES (2, 170);
-- 等待事務(wù)A的間隙鎖
-- 死鎖!

六、性能優(yōu)化與最佳實(shí)踐

6.1 隔離級(jí)別選擇建議

-- 選擇合適隔離級(jí)別的決策流程
/**
 * 決策樹(shù):
 * 1. 需要避免臟讀?是 → 至少READ COMMITTED
 * 2. 需要避免不可重復(fù)讀?是 → 至少REPEATABLE READ
 * 3. 需要避免幻讀?是 → SERIALIZABLE
 * 4. 并發(fā)性能要求高?是 → 考慮降低隔離級(jí)別
 * 5. 有明確的鎖策略?是 → 可以使用較低隔離級(jí)別+手動(dòng)加鎖
 */
-- 各隔離級(jí)別適用場(chǎng)景總結(jié):
-- READ UNCOMMITTED: 統(tǒng)計(jì)類只讀查詢,允許臟數(shù)據(jù)
-- READ COMMITTED: 大多數(shù)OLTP系統(tǒng),需要實(shí)時(shí)數(shù)據(jù)
-- REPEATABLE READ: 財(cái)務(wù)系統(tǒng),對(duì)賬系統(tǒng),需要穩(wěn)定視圖
-- SERIALIZABLE: 銀行核心交易,需要絕對(duì)一致性

6.2 事務(wù)設(shè)計(jì)最佳實(shí)踐

-- 實(shí)踐1:保持事務(wù)短小
-- 錯(cuò)誤示例:長(zhǎng)事務(wù)
START TRANSACTION;
-- 復(fù)雜業(yè)務(wù)邏輯
-- 網(wǎng)絡(luò)調(diào)用
-- 文件操作
-- 大量計(jì)算
COMMIT; -- 事務(wù)持續(xù)時(shí)間太長(zhǎng)
-- 正確示例:拆分事務(wù)
-- 事務(wù)1:數(shù)據(jù)準(zhǔn)備
START TRANSACTION;
INSERT INTO temp_data SELECT ...;
COMMIT;
-- 事務(wù)2:業(yè)務(wù)處理
START TRANSACTION;
UPDATE ...;
COMMIT;
-- 實(shí)踐2:避免在事務(wù)中執(zhí)行SELECT ... FOR UPDATE時(shí)掃描大量數(shù)據(jù)
-- 錯(cuò)誤示例
START TRANSACTION;
SELECT * FROM orders WHERE create_time > '2023-01-01' FOR UPDATE;
-- 可能鎖定大量行,導(dǎo)致性能問(wèn)題
-- 正確示例:分批處理
DECLARE done INT DEFAULT FALSE;
DECLARE batch_size INT DEFAULT 100;
DECLARE offset INT DEFAULT 0;
WHILE NOT done DO
    START TRANSACTION;
    SELECT * FROM orders 
    WHERE create_time > '2023-01-01' 
    LIMIT batch_size OFFSET offset 
    FOR UPDATE;
    -- 處理邏輯
    COMMIT;
    SET offset = offset + batch_size;
    -- 檢查是否完成
END WHILE;

6.3 監(jiān)控與調(diào)優(yōu)

-- 監(jiān)控當(dāng)前事務(wù)
SELECT * FROM information_schema.INNODB_TRX;
-- 監(jiān)控鎖信息
SELECT * FROM information_schema.INNODB_LOCKS;
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
-- 監(jiān)控長(zhǎng)事務(wù)
SELECT 
    trx_id,
    trx_started,
    TIMESTAMPDIFF(SECOND, trx_started, NOW()) as duration_seconds,
    trx_state,
    trx_query
FROM information_schema.INNODB_TRX
WHERE TIMESTAMPDIFF(SECOND, trx_started, NOW()) > 60  -- 超過(guò)60秒的事務(wù)
ORDER BY trx_started;
-- 設(shè)置長(zhǎng)事務(wù)超時(shí)
SET SESSION innodb_lock_wait_timeout = 50;  -- 鎖等待超時(shí)50秒
SET SESSION innodb_rollback_on_timeout = ON; -- 超時(shí)自動(dòng)回滾

七、實(shí)際案例分析

7.1 電商秒殺系統(tǒng)

-- 秒殺場(chǎng)景下的庫(kù)存扣減優(yōu)化
-- 表設(shè)計(jì)
CREATE TABLE seckill_stock (
    product_id BIGINT PRIMARY KEY,
    stock INT NOT NULL,
    version INT DEFAULT 0,
    sale_date DATE
);
-- 方案1:使用樂(lè)觀鎖+重試機(jī)制
DELIMITER //
CREATE PROCEDURE seckill_purchase(
    IN p_product_id BIGINT,
    IN p_user_id BIGINT,
    OUT p_result INT
)
BEGIN
    DECLARE v_stock INT;
    DECLARE v_version INT;
    DECLARE retry_count INT DEFAULT 0;
    DECLARE max_retry INT DEFAULT 3;
    purchase_retry: REPEAT
        START TRANSACTION;
        -- 查詢當(dāng)前庫(kù)存和版本
        SELECT stock, version INTO v_stock, v_version
        FROM seckill_stock 
        WHERE product_id = p_product_id 
        FOR UPDATE;  -- 悲觀鎖,防止其他事務(wù)同時(shí)修改
        IF v_stock <= 0 THEN
            ROLLBACK;
            SET p_result = 0; -- 庫(kù)存不足
            LEAVE purchase_retry;
        END IF;
        -- 更新庫(kù)存
        UPDATE seckill_stock 
        SET stock = stock - 1,
            version = version + 1
        WHERE product_id = p_product_id 
          AND version = v_version;
        -- 檢查是否更新成功
        IF ROW_COUNT() = 1 THEN
            -- 創(chuàng)建訂單
            INSERT INTO seckill_order(product_id, user_id, create_time)
            VALUES (p_product_id, p_user_id, NOW());
            COMMIT;
            SET p_result = 1; -- 成功
            LEAVE purchase_retry;
        ELSE
            ROLLBACK;
            SET retry_count = retry_count + 1;
            -- 等待隨機(jī)時(shí)間后重試
            DO SLEEP(RAND() * 0.1);
        END IF;
    UNTIL retry_count >= max_retry END REPEAT;
    IF retry_count >= max_retry THEN
        SET p_result = -1; -- 重試失敗
    END IF;
END//
DELIMITER ;

7.2 金融對(duì)賬系統(tǒng)

-- 對(duì)賬系統(tǒng)需要數(shù)據(jù)一致性
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
START TRANSACTION;
-- 記錄對(duì)賬開(kāi)始時(shí)間
SET @reconcile_time = NOW();
-- 創(chuàng)建對(duì)賬快照表
CREATE TEMPORARY TABLE reconcile_snapshot AS
SELECT 
    a.account_no,
    a.balance as db_balance,
    b.balance as external_balance
FROM account a
LEFT JOIN external_system b ON a.account_no = b.account_no
WHERE a.update_time <= @reconcile_time;
-- 執(zhí)行對(duì)賬邏輯
SELECT 
    account_no,
    db_balance,
    external_balance,
    CASE 
        WHEN ABS(db_balance - external_balance) > 0.01 THEN 'MISMATCH'
        ELSE 'MATCH'
    END as status
FROM reconcile_snapshot;
-- 記錄對(duì)賬結(jié)果
INSERT INTO reconcile_log(reconcile_time, total_count, mismatch_count)
SELECT 
    @reconcile_time,
    COUNT(*),
    SUM(CASE WHEN ABS(db_balance - external_balance) > 0.01 THEN 1 ELSE 0 END)
FROM reconcile_snapshot;
COMMIT;

7.3 社交系統(tǒng)點(diǎn)贊功能

-- 點(diǎn)贊功能,需要避免重復(fù)點(diǎn)贊
CREATE TABLE likes (
    id BIGINT PRIMARY KEY AUTO_INCREMENT,
    user_id BIGINT NOT NULL,
    post_id BIGINT NOT NULL,
    create_time DATETIME DEFAULT CURRENT_TIMESTAMP,
    UNIQUE KEY uk_user_post (user_id, post_id)  -- 唯一約束防止重復(fù)
);
-- 點(diǎn)贊操作
START TRANSACTION;
-- 嘗試插入,利用唯一約束保證冪等性
INSERT IGNORE INTO likes(user_id, post_id) 
VALUES (123, 456);
-- 如果插入成功,更新帖子點(diǎn)贊數(shù)
IF ROW_COUNT() > 0 THEN
    UPDATE posts 
    SET like_count = like_count + 1 
    WHERE id = 456;
END IF;
COMMIT;
-- 取消點(diǎn)贊
START TRANSACTION;
DELETE FROM likes 
WHERE user_id = 123 AND post_id = 456;
-- 如果刪除成功,更新帖子點(diǎn)贊數(shù)
IF ROW_COUNT() > 0 THEN
    UPDATE posts 
    SET like_count = GREATEST(0, like_count - 1) 
    WHERE id = 456;
END IF;
COMMIT;

八、常見(jiàn)問(wèn)題與陷阱

8.1 自動(dòng)提交陷阱

-- MySQL默認(rèn)autocommit=1,每條語(yǔ)句都是一個(gè)事務(wù)
-- 可能導(dǎo)致意想不到的問(wèn)題
-- 關(guān)閉自動(dòng)提交
SET autocommit = 0;
-- 顯式控制事務(wù)
START TRANSACTION;
-- 業(yè)務(wù)邏輯
COMMIT; -- 或 ROLLBACK;
-- 恢復(fù)自動(dòng)提交
SET autocommit = 1;

8.2 隱式提交操作

-- 以下語(yǔ)句會(huì)隱式提交當(dāng)前事務(wù):
-- 1. DDL語(yǔ)句(CREATE, ALTER, DROP等)
-- 2. 用戶權(quán)限管理語(yǔ)句
-- 3. 鎖表語(yǔ)句(LOCK TABLES, UNLOCK TABLES)
-- 錯(cuò)誤示例
START TRANSACTION;
UPDATE account SET balance = balance - 100 WHERE id = 1;
-- 這個(gè)語(yǔ)句會(huì)提交事務(wù)!
CREATE INDEX idx_balance ON account(balance);
UPDATE account SET balance = balance + 100 WHERE id = 2;
-- 如果這里出錯(cuò),第一個(gè)UPDATE已經(jīng)提交,無(wú)法回滾!
COMMIT;

8.3 大事務(wù)問(wèn)題

-- 大事務(wù)可能導(dǎo)致的問(wèn)題:
-- 1. 鎖持有時(shí)間長(zhǎng),阻塞其他事務(wù)
-- 2. 產(chǎn)生大量undo log,占用磁盤
-- 3. 主從復(fù)制延遲
-- 4. 回滾時(shí)間長(zhǎng)
-- 解決方案:拆分大事務(wù)
-- 原始大事務(wù)
START TRANSACTION;
-- 處理10萬(wàn)條數(shù)據(jù)
UPDATE large_table SET status = 1 WHERE condition;
COMMIT; -- 可能執(zhí)行幾分鐘
-- 拆分為小批次
SET autocommit = 0;
SET @rows_affected = 1;
WHILE @rows_affected > 0 DO
    START TRANSACTION;
    UPDATE large_table 
    SET status = 1 
    WHERE condition 
    AND status = 0 
    LIMIT 1000;
    SET @rows_affected = ROW_COUNT();
    COMMIT;
    -- 短暫暫停,減少對(duì)系統(tǒng)影響
    DO SLEEP(0.1);
END WHILE;
SET autocommit = 1;

總結(jié)

MySQL事務(wù)隔離級(jí)別的選擇需要權(quán)衡一致性、并發(fā)性能和數(shù)據(jù)準(zhǔn)確性:

  • READ UNCOMMITTED:幾乎不用,除非特殊場(chǎng)景
  • READ COMMITTED:適合大多數(shù)OLTP系統(tǒng),需要實(shí)時(shí)數(shù)據(jù)
  • REPEATABLE READ(MySQL默認(rèn)):需要穩(wěn)定數(shù)據(jù)視圖的場(chǎng)景
  • SERIALIZABLE:需要絕對(duì)一致性的關(guān)鍵系統(tǒng)

最佳實(shí)踐建議

  • 優(yōu)先使用REPEATABLE READ,配合合理的鎖策略
  • 事務(wù)盡量短小,避免長(zhǎng)事務(wù)
  • 合理使用索引,減少鎖競(jìng)爭(zhēng)
  • 監(jiān)控長(zhǎng)事務(wù)和死鎖,及時(shí)優(yōu)化
  • 根據(jù)業(yè)務(wù)特點(diǎn)選擇合適隔離級(jí)別,不要盲目追求高隔離級(jí)別

性能優(yōu)化要點(diǎn)

  • 熱點(diǎn)數(shù)據(jù)使用樂(lè)觀鎖+重試機(jī)制
  • 批量操作使用分批次處理
  • 避免事務(wù)中的網(wǎng)絡(luò)I/O和復(fù)雜計(jì)算
  • 使用從庫(kù)進(jìn)行統(tǒng)計(jì)查詢,減輕主庫(kù)壓力

到此這篇關(guān)于MySQL 事務(wù)隔離級(jí)別及實(shí)際業(yè)務(wù)問(wèn)題詳解的文章就介紹到這了,更多相關(guān)mysql事務(wù)隔離級(jí)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 深入解析mysql中distinct的用法

    深入解析mysql中distinct的用法

    本文深入解析了MySQL中DISTINCT關(guān)鍵字的功能,包括其作用、原理及使用方法,DISTINCT用于去除重復(fù)記錄,提高數(shù)據(jù)查詢效率,文章通過(guò)實(shí)例展示了如何對(duì)單個(gè)或多個(gè)字段進(jìn)行去重,并結(jié)合COUNT函數(shù)使用,感興趣的朋友跟隨小編一起看看吧
    2025-09-09
  • Mysql內(nèi)置函數(shù)的實(shí)現(xiàn)示例

    Mysql內(nèi)置函數(shù)的實(shí)現(xiàn)示例

    mysql內(nèi)置了很多的函數(shù),本文主要介紹了Mysql內(nèi)置函數(shù)的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2024-07-07
  • mysql常用函數(shù)之group_concat()、group by、count()、case when then的使用

    mysql常用函數(shù)之group_concat()、group by、count()、case whe

    本文主要介紹了mysql常用函數(shù)之group_concat()、group by、count()、case when then的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-01-01
  • MySQL索引的缺點(diǎn)以及MySQL索引在實(shí)際操作中有哪些事項(xiàng)

    MySQL索引的缺點(diǎn)以及MySQL索引在實(shí)際操作中有哪些事項(xiàng)

    以下的文章主要介紹的是MySQL索引的缺點(diǎn)以及MySQL索引在實(shí)際操作中有哪些事項(xiàng)是值得我們大家注意的,我們大家可能不知道過(guò)多的對(duì)索引進(jìn)行使用將會(huì)造成濫用,需要的朋友可以了解下
    2012-12-12
  • 關(guān)于MySQL存取圖片的三種方式(含源碼示例)

    關(guān)于MySQL存取圖片的三種方式(含源碼示例)

    最近在做小程序的后臺(tái),要求將小程序用戶以u(píng)pload方法上傳的圖片保存到mysql數(shù)據(jù)庫(kù)中,然后機(jī)緣巧合三種方式都試了,所以專門整理出來(lái)分享給大家,可能有的地方說(shuō)的不太對(duì),歡迎大家?guī)图m正,需要的朋友可以參考下
    2024-04-04
  • MySQL?存儲(chǔ)引擎概覽(最新推薦)

    MySQL?存儲(chǔ)引擎概覽(最新推薦)

    MySQL?的存儲(chǔ)引擎(Storage?Engine)決定了數(shù)據(jù)如何存儲(chǔ)、索引如何組織、事務(wù)是否支持、鎖的粒度、崩潰恢復(fù)能力等,是數(shù)據(jù)庫(kù)性能與可靠性的核心,本文給大家介紹MySQL?存儲(chǔ)引擎概覽,感興趣的朋友跟隨小編一起看看吧
    2026-02-02
  • 給mysql數(shù)據(jù)庫(kù)的字段設(shè)默認(rèn)值方式

    給mysql數(shù)據(jù)庫(kù)的字段設(shè)默認(rèn)值方式

    這篇文章主要介紹了給mysql數(shù)據(jù)庫(kù)的字段設(shè)默認(rèn)值方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • MySQL外鍵關(guān)聯(lián)操作的實(shí)現(xiàn)

    MySQL外鍵關(guān)聯(lián)操作的實(shí)現(xiàn)

    本文主要介紹了MySQL外鍵關(guān)聯(lián)操作的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • MySQL server has gone away 問(wèn)題的解決方法

    MySQL server has gone away 問(wèn)題的解決方法

    MySQL server has gone away 問(wèn)題解決方法,需要的朋友可以參考下。
    2010-06-06
  • MySQL判斷非空和非空函數(shù)

    MySQL判斷非空和非空函數(shù)

    本文詳細(xì)介紹了MySQL中使用ISNULL,ISNOTNULL,COALESCE,IFNULL和NULLIF函數(shù)判斷和處理非空字段,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-12-12

最新評(píng)論

海伦市| 金坛市| 稻城县| 定兴县| 白山市| 崇礼县| 西青区| 天门市| 巴林左旗| 长白| 汨罗市| 阿克陶县| 肥城市| 营山县| 耿马| 龙江县| 丰城市| 万安县| 象州县| 溆浦县| 周至县| 北川| 巩留县| 绥江县| 浪卡子县| 南靖县| 哈巴河县| 渭源县| 祁东县| 安国市| 安化县| 陇川县| 石首市| 建水县| 阿拉善左旗| 永善县| 怀远县| 北流市| 若羌县| 深州市| 临湘市|