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

MySQL的事務(wù)機(jī)制與并發(fā)讀異常示例詳解

 更新時(shí)間:2025年11月01日 13:39:34   作者:洲覆  
數(shù)據(jù)庫事務(wù)是一組數(shù)據(jù)庫操作,它們被視為一個(gè)單獨(dú)的工作單元,要么全部成功,要么全部失敗,這篇文章主要介紹了MySQL事務(wù)機(jī)制與并發(fā)讀異常的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

一、事務(wù)

事務(wù)是用戶定義的一系列操作,這些操作需要讓MySQL視為一個(gè)整體執(zhí)行,這些操作要么都做,要么都不做,是一個(gè)不可分割的工作單位。

1.1 概念

前提:有并發(fā)連接訪問,多條連接,并發(fā)執(zhí)行 sql 語句

事務(wù)是用戶定義的不可分割的操作序列,是并發(fā)控制的最小單元,這些操作要么全部執(zhí)行(提交),要么全部不執(zhí)行(回滾)。

目的:事務(wù)將數(shù)據(jù)庫從一種一致性狀態(tài)轉(zhuǎn)換為另一種一致性狀態(tài);保證系統(tǒng)的數(shù)據(jù)完整性與可靠性。

舉個(gè)例子:在銀行轉(zhuǎn)賬中,“A 扣款 + B 收款”就是一個(gè)典型事務(wù),要么兩個(gè)操作都成功,要么都失敗,不能只執(zhí)行一半。

組成:

  • 簡單事務(wù):可由單條 SQL 語句組成(如 UPDATE students SET age=26 WHERE id=15);
  • 復(fù)雜事務(wù):可由一組關(guān)聯(lián)的 SQL 語句組成(如轉(zhuǎn)賬場(chǎng)景中的 “扣款 + 收款” 兩條更新語句)。

特征:

  • 在數(shù)據(jù)庫提交事務(wù)時(shí),可以確保要么所有修改都已經(jīng)保存,要么所有修改都不保存;
  • 事務(wù)是訪問并更新數(shù)據(jù)庫各種數(shù)據(jù)項(xiàng)的一個(gè)程序執(zhí)行單元。
  • 在 MySQL innodb 下,單條語句都具備事務(wù);可以通過 set autocommit = 0; 設(shè)置當(dāng)前會(huì)話手動(dòng)提交;

1.2 事務(wù)控制語句

  • begin/start transaction
  • commit
  • rollback
-- 顯示開啟事務(wù)
START TRANSACTION | BEGIN

-- 提交事務(wù),并使得已對(duì)數(shù)據(jù)庫做的所有修改持久化
COMMIT

-- 回滾事務(wù),結(jié)束用戶的事務(wù),并撤銷正在進(jìn)行的所有未提交的修改
ROLLBACK

-- 創(chuàng)建一個(gè)保存點(diǎn),一個(gè)事務(wù)可以有多個(gè)保存點(diǎn)
SAVEPOINT identifier

-- 刪除一個(gè)保存點(diǎn)
RELEASE SAVEPOINT identifier

-- 事務(wù)回滾到保存點(diǎn)
ROLLBACK TO [SAVEPOINT] identifier

1.3 ACID特性

① 原子性(A)

事務(wù)是最小執(zhí)行單元,事務(wù)操作要么都做(提交),要么都不做(回滾);MySQL 通過 undo log 實(shí)現(xiàn)回滾,它記錄了每個(gè)操作的反向操作,從而在異常時(shí)恢復(fù)數(shù)據(jù)。

② 一致性(C)

事務(wù)執(zhí)行前后,數(shù)據(jù)庫必須處于一致狀態(tài)。

  • 數(shù)據(jù)庫完整性約束(唯一、非空等)
  • 邏輯上的一致性:用戶定義的一系列操作,需要數(shù)據(jù)庫視為一個(gè)整體,這是用戶定義的

事務(wù)執(zhí)行前后,數(shù)據(jù)庫的 “完整性約束”(如唯一鍵、非空約束)與 “邏輯一致性”(如轉(zhuǎn)賬總金額不變)不能被破壞。一致性由原子性、隔離性、持久性共同保障。

③ 隔離性(I)

描述:控制 “多個(gè)并發(fā)事務(wù)的相互影響程度”;各自運(yùn)行的環(huán)境隔離。

多個(gè)事務(wù)并發(fā)執(zhí)行時(shí),應(yīng)互不干擾。

一個(gè)事務(wù)內(nèi)部的操作和數(shù)據(jù),在其提交前對(duì)其他事務(wù)是不可見的。

InnoDB 用 MVCC(多版本并發(fā)控制) 處理 “讀 - 寫并發(fā)”(讀操作通過版本鏈讀歷史數(shù)據(jù),無需加鎖);用鎖(表鎖、頁鎖、行鎖等) 處理 “寫 - 寫并發(fā)”(寫操作加鎖避免沖突)。

MVCC 是多版本并發(fā)控制,主要解決一致性非鎖定讀,通過記錄和獲取行版本,而不是使用鎖來限制讀操作,從而實(shí)現(xiàn)高效并發(fā)讀性能。鎖用來處理并發(fā) DML 操作;數(shù)據(jù)庫中提供粒度鎖的策略,針對(duì)表(聚集索引 B+ 樹)、頁(聚集索引 B+ 樹葉子節(jié)點(diǎn))、行(葉子節(jié)點(diǎn)當(dāng)中某一段記錄行)三種粒度加鎖;

不同隔離級(jí)別會(huì)導(dǎo)致不同并發(fā)現(xiàn)象(臟讀、不可重復(fù)讀、幻讀)

④ 持久性(D)

事務(wù)一旦提交,數(shù)據(jù)的修改就會(huì)永久保存

無論系統(tǒng)宕機(jī)、斷電還是重啟,數(shù)據(jù)都能通過 redo log(重做日志) 恢復(fù)。

redo log 記錄的是物理層面的“頁修改”信息,比如修改了哪個(gè)數(shù)據(jù)頁、偏移量、內(nèi)容等,保證事務(wù)的最終落盤。

1.4 隔離級(jí)別

ISO 和 ANIS SQL 標(biāo)準(zhǔn)制定了四種事務(wù)隔離級(jí)別的標(biāo)準(zhǔn),各數(shù)據(jù)庫廠商在正確性和性能之間做了妥協(xié),并沒有嚴(yán)格遵循這些標(biāo)準(zhǔn);MySQL innodb默認(rèn)支持的隔離級(jí)別是 REPEATABLE READ;

數(shù)據(jù)庫標(biāo)準(zhǔn)(ISO/ANSI SQL)定義了四種隔離級(jí)別,從低到高依次是:

隔離級(jí)別說明讀操作寫操作并發(fā)現(xiàn)象性能特點(diǎn)
READ UNCOMMITTED讀未提交,讀不加鎖,性能最高但風(fēng)險(xiǎn)最大不加鎖,直接讀最新數(shù)據(jù)自動(dòng)加 X 鎖可能臟讀、不可重復(fù)讀、幻讀性能最高,安全性最差
READ COMMITTED讀已提交;讀取最新已提交版本(支持 MVCC通過 MVCC 讀已提交數(shù)據(jù)自動(dòng)加 X 鎖避免臟讀,仍可能不可重復(fù)讀、幻讀性能較好
REPEATABLE READ(默認(rèn))可重復(fù)讀,事務(wù)期間讀到的是事務(wù)開始時(shí)的數(shù)據(jù)快照(支持 MVCC通過 MVCC 讀事務(wù)開始前版本自動(dòng)加 X 鎖避免臟讀、不可重復(fù)讀,但仍可能幻讀;MySQL 已基本避免幻讀性能適中
SERIALIZABLE可串行化;所有事務(wù)順序執(zhí)行S 鎖(Next-Key Lock)X 鎖完全避免并發(fā)異常性能最差,最安全

1.5 命令

-- 設(shè)置隔離級(jí)別
SET [GLOBAL | SESSION] TRANSACTION ISOLATION LEVEL REPEATABLE READ;

-- 或者采用下面的方式設(shè)置隔離級(jí)別
SET @@tx_isolation = 'REPEATABLE READ';
SET @@global.tx_isolation = 'REPEATABLE READ';

-- 查看全局隔離級(jí)別
SELECT @@global.tx_isolation;

-- 查看當(dāng)前會(huì)話隔離級(jí)別
SELECT @@session.tx_isolation;
SELECT @@tx_isolation;

-- 手動(dòng)給讀加 S 鎖
SELECT ... LOCK IN SHARE MODE;

-- 手動(dòng)給讀加 X 鎖
SELECT ... FOR UPDATE;

-- 查看當(dāng)前鎖信息
SELECT * FROM information_schema.innodb_locks;

二、并發(fā)讀異常

2.1 臟讀

事務(wù)(A)可以讀到另外一個(gè)事務(wù)(B)中未提交的數(shù)據(jù);也就是事務(wù)A讀到臟數(shù)據(jù);在讀寫分離的場(chǎng)景下,可以將 slave 節(jié)點(diǎn)設(shè)置為 READ UNCOMMITTED;此時(shí)臟讀不影響,在 slave 上查詢并不需要特別精準(zhǔn)的返回值。

示例

seqsession Asession B
1SET @@tx_isolation='READ UNCOMMITTED';SET @@tx_isolation='READ UNCOMMITTED';
2BEGIN;
3UPDATE account_t SET money = money - 100 WHERE name = 'A';
4BEGIN;
5SELECT money FROM account_t WHERE name = 'A';
6SELECT money FROM account_t WHERE name = 'B';
7UPDATE account_t SET money = money - 100 WHERE name = 'B';
8COMMITCOMMIT

2.2 不可重復(fù)讀

事務(wù)(A) 可以讀到另外一個(gè)事務(wù)(B)中提交的數(shù)據(jù);通常發(fā)生在一個(gè)事務(wù)中兩次讀到的數(shù)據(jù)是不一樣的情況;不可重復(fù)讀在隔離級(jí)別 READ COMMITTED 存在。一般而言,不可重復(fù)讀的問題是可以接受的,因?yàn)樽x到已經(jīng)提交的數(shù)據(jù),一般不會(huì)帶來很大的問題,所以很多廠商(如Oracle、SQL Server)默認(rèn)隔離級(jí)別就是 READ COMMITTED;

示例

seqsession Asession B
1SET @@tx_isolation='READ COMMITTED';SET @@tx_isolation='READ COMMITTED';
2BEGIN;BEGIN;
3SELECT money FROM account_t WHERE name = 'A';
4UPDATE account_t SET money = money - 100 WHERE name = 'A';
5COMMIT;SELECT money FROM account_t WHERE name = 'A';
6COMMIT;

2.3 幻讀

兩次讀取同一個(gè)范圍內(nèi)的記錄得到的結(jié)果集不一樣;快照讀和當(dāng)前讀不一致;

例如:以 name 為唯一鍵的表,一個(gè)事務(wù)中查詢 select * from t where name = 'gulu'; 不存在,接下來 insert into t(name) values ('gulu'); 出現(xiàn)錯(cuò)誤,因?yàn)榇藭r(shí)另外一個(gè)事務(wù)也執(zhí)行了 insert 操作;

幻讀在隔離級(jí)別 REPEATABLE READ 及以下存在;但是可以在 REPEATABLE READ 級(jí)別下通過讀加鎖(使用 next-key locking)解決;

示例

seqsession Asession B
1SET @@tx_isolation='REPEATABLE READ';SET @@tx_isolation='REPEATABLE READ';
2BEGIN;BEGIN;
3SELECT * FROM account_t WHERE id >= 2;
4INSERT INTO account_t(id,name,money) VALUES (4,'D',1000);
5COMMIT;
6INSERT INTO account_t(id,name,money) VALUES (4,'D',1000);
7COMMIT;

解決:給讀操作加鎖

seqsession Asession B
1SET @@tx_isolation='REPEATABLE READ';SET @@tx_isolation='REPEATABLE READ';
2BEGIN;BEGIN;
3SELECT * FROM account_t WHERE id >= 2 lock in share mode;
4INSERT INTO account_t(id,name,money) VALUES (4,'D',1000);
5COMMIT;
6SELECT * FROM account_t WHERE id >= 2;
7COMMIT;

2.4 丟失更新

臟讀、不可重復(fù)讀、幻讀都是一個(gè)事務(wù)寫,一個(gè)事務(wù)讀,由于一個(gè)事務(wù)的寫導(dǎo)致另一個(gè)事務(wù)讀到了不該讀的數(shù)據(jù);丟失更新是兩個(gè)事務(wù)都是寫;丟失更新分為提交覆蓋和回滾覆蓋;回滾覆蓋數(shù)據(jù)庫拒絕不可能產(chǎn)生,重點(diǎn)關(guān)注提交覆蓋;

示例

seqsession Asession B
1SET @@tx_isolation='REPEATABLE READ';SET @@tx_isolation='REPEATABLE READ';
2BEGIN;BEGIN;
3SELECT money FROM account_t WHERE name = 'A';
4SELECT money FROM account_t WHERE name = 'A';
5UPDATE account_t SET money = 1000+100 WHERE name = 'A';
6COMMIT;
7UPDATE account_t SET money = 1000-100 WHERE name = 'A';
8COMMIT;

2.5 隔離級(jí)別下并發(fā)讀異常

隔離級(jí)別回滾覆蓋臟讀不可重復(fù)讀幻讀提交覆蓋
READ UNCOMMITTEDnoyesyesyesyes
READ COMMITTEDnonoyesyesyes
REPEATABLE READnononoyes(手動(dòng)加鎖)yes(手動(dòng)加鎖)
SERIALIZABLEnonononono

2.6 區(qū)別

  • 臟讀和不可重復(fù)讀的區(qū)別在于,臟讀是讀取了另一個(gè)事務(wù)未提交的數(shù)據(jù),而不可重復(fù)讀是讀取了另一個(gè)事務(wù)提交之后的修改;本質(zhì)上都是其他事務(wù)的修改影響了本事務(wù)的讀??;
  • 不可重復(fù)讀和幻讀比較類似;不可重復(fù)讀是兩次讀取同一條記錄,得到不一樣的結(jié)果;而幻讀是兩次讀取同一個(gè)范圍內(nèi)的記錄得到的結(jié)果集不一樣(可能不同個(gè)數(shù),也可能相同個(gè)數(shù)內(nèi)容不一樣,比如x一行后又添加新行);不可重復(fù)讀是因?yàn)槠渌聞?wù)進(jìn)行了 update 操作,幻讀是因?yàn)槠渌聞?wù)進(jìn)行了 insert 或者 delete 操作。

三、Redo Log(重做日志)

在 MySQL InnoDB 存儲(chǔ)引擎中,Redo Log 是實(shí)現(xiàn)事務(wù)持久性的核心機(jī)制。
簡單來說,它用來保證事務(wù)提交后,即使系統(tǒng)宕機(jī),也能通過日志恢復(fù)到正確狀態(tài)。

在事務(wù)執(zhí)行過程中,數(shù)據(jù)的修改首先發(fā)生在內(nèi)存中的 Buffer Pool,但內(nèi)存數(shù)據(jù)存在易失性,一旦斷電或崩潰就會(huì)丟失。為了防止這種情況,InnoDB 設(shè)計(jì)了 Redo Log 來記錄數(shù)據(jù)的物理修改。

Redo Log 由兩部分組成:

  • redo log buffer(內(nèi)存中):暫存事務(wù)執(zhí)行時(shí)產(chǎn)生的日志;
  • redo log file(磁盤文件):持久化存儲(chǔ)日志記錄。

在事務(wù)提交(COMMIT)的過程中,必須先將 redo log buffer 寫入磁盤文件(刷盤),確認(rèn)日志已經(jīng)持久化后,事務(wù)才算真正提交成功。這就是事務(wù)的 WAL(Write-Ahead Logging,預(yù)寫日志)機(jī)制 —— 先寫日志,后寫數(shù)據(jù)。

Redo Log 的特性與作用

  • 記錄內(nèi)容: 頁號(hào)、頁偏移量、修改內(nèi)容(物理變化);
  • 寫入方式: 順序?qū)懭耄阅芨撸?/li>
  • 使用場(chǎng)景: 正常運(yùn)行時(shí)只寫不讀,只有在 宕機(jī)恢復(fù) 時(shí)才會(huì)被讀取,用于將數(shù)據(jù)恢復(fù)到最近一次提交的狀態(tài)。
  • 作用總結(jié): 確保了數(shù)據(jù)的持久性(D)

四、Undo Log(回滾日志)

與 Redo Log 相對(duì),Undo Log 用來實(shí)現(xiàn)事務(wù)的原子性(Atomicity)和支持 MVCC(多版本并發(fā)控制)
如果 Redo Log 是“向前重做”,Undo Log 就是“向后回滾”。

當(dāng)事務(wù)執(zhí)行時(shí),InnoDB 會(huì)為每一次數(shù)據(jù)修改生成相應(yīng)的 Undo 記錄,用來保存修改前的數(shù)據(jù)版本。這些 Undo 日志存儲(chǔ)在 共享表空間(Undo Tablespace) 中。

在事務(wù)回滾(ROLLBACK)時(shí),系統(tǒng)會(huì)根據(jù) Undo Log 執(zhí)行反向操作,把數(shù)據(jù)邏輯地恢復(fù)到修改前的狀態(tài):

  • 若事務(wù)執(zhí)行了 INSERT,回滾時(shí)會(huì)執(zhí)行 DELETE;
  • 若事務(wù)執(zhí)行了 DELETE,回滾時(shí)會(huì)執(zhí)行 INSERT
  • 若事務(wù)執(zhí)行了 UPDATE,回滾時(shí)會(huì)執(zhí)行相反方向的 UPDATE

Undo Log 的特性與作用

  1. 保證事務(wù)原子性:
    如果事務(wù)執(zhí)行中途失敗,可以根據(jù) Undo Log 將已執(zhí)行的部分操作撤銷,確保“要么全部成功,要么全部失敗”。

  2. 支撐 MVCC(多版本并發(fā)控制):
    Undo Log 記錄了每行數(shù)據(jù)的歷史版本,通過它 MySQL 可以在不同事務(wù)中讀取到不同時(shí)刻的數(shù)據(jù)快照,從而實(shí)現(xiàn)非鎖定讀。

  3. 記錄形式: Undo Log 是邏輯日志(記錄操作邏輯),不同于 Redo Log 的物理頁修改。

總結(jié) 

到此這篇關(guān)于MySQL事務(wù)機(jī)制與并發(fā)讀異常的文章就介紹到這了,更多相關(guān)MySQL事務(wù)與并發(fā)讀異常內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MySQL數(shù)據(jù)庫入門之多實(shí)例配置方法詳解

    MySQL數(shù)據(jù)庫入門之多實(shí)例配置方法詳解

    這篇文章主要介紹了MySQL數(shù)據(jù)庫入門之多實(shí)例配置方法,結(jié)合實(shí)例形式分析了MySQL數(shù)據(jù)庫多實(shí)例配置相關(guān)概念、原理、操作方法與注意事項(xiàng),需要的朋友可以參考下
    2020-05-05
  • MySql實(shí)現(xiàn)分布式鎖的示例代碼

    MySql實(shí)現(xiàn)分布式鎖的示例代碼

    本文主要介紹了使用MySQL實(shí)現(xiàn)分布式鎖,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-12-12
  • MySQL去除重疊時(shí)間求時(shí)間差和的實(shí)現(xiàn)

    MySQL去除重疊時(shí)間求時(shí)間差和的實(shí)現(xiàn)

    在生產(chǎn)中常常出現(xiàn)計(jì)算兩個(gè)時(shí)間差的業(yè)務(wù),比如總宕機(jī)時(shí)間、總開通會(huì)員時(shí)間等,本文就詳細(xì)的來介紹一下如何計(jì)算,感興趣的可以了解一下
    2021-08-08
  • MySQL優(yōu)化器統(tǒng)計(jì)信息的配置指南

    MySQL優(yōu)化器統(tǒng)計(jì)信息的配置指南

    在 MySQL 中,查詢優(yōu)化器(Query Optimizer) 負(fù)責(zé)決定執(zhí)行 SQL 語句的最佳方式,比如是否使用某個(gè)索引、用哪個(gè)索引、是否進(jìn)行全表掃描等,本文給大家介紹了MySQL優(yōu)化器統(tǒng)計(jì)信息的配置指南,需要的朋友可以參考下
    2025-09-09
  • MySql事務(wù)原理介紹及特性

    MySql事務(wù)原理介紹及特性

    事務(wù)就是一組DML語句組成,這些語句在邏輯上存在相關(guān)性,這一組DML語句要么全部成功,要么全部失敗,是一個(gè)整體。MySQL提供一種機(jī)制,保證我們達(dá)到這樣的效果。事務(wù)還規(guī)定不同的客戶端看到的數(shù)據(jù)是不相同的
    2022-09-09
  • Mysql用戶授權(quán)(GRANT)語法及示例解讀

    Mysql用戶授權(quán)(GRANT)語法及示例解讀

    這篇文章主要介紹了Mysql用戶授權(quán)(GRANT)語法及示例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • 殺死MySQL進(jìn)程的多種方法實(shí)現(xiàn)

    殺死MySQL進(jìn)程的多種方法實(shí)現(xiàn)

    本文主要介紹了殺死MySQL進(jìn)程的方法實(shí)現(xiàn),包括查看進(jìn)程列表和通過KILL語句終止指定進(jìn)程或查詢,具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-06-06
  • mysql父子集查詢(根據(jù)父級(jí)查詢所有子集)

    mysql父子集查詢(根據(jù)父級(jí)查詢所有子集)

    某些場(chǎng)景可能需要維護(hù)一些有父子關(guān)系的數(shù)據(jù),本文主要介紹了mysql父子集查詢(根據(jù)父級(jí)查詢所有子集),具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • 淺談sql語句中GROUP BY 和 HAVING的使用方法

    淺談sql語句中GROUP BY 和 HAVING的使用方法

    GROUP BY語句和HAVING語句,經(jīng)過研究和練習(xí),終于明白如何使用了,在此記錄一下同時(shí)添加了一個(gè)自己舉的小例子,通過寫這篇文章來加深下自己學(xué)習(xí)的效果,還能和大家分享下,同時(shí)也方便以后查閱,一舉多得,下面由小編來和大家一起學(xué)習(xí)
    2019-05-05
  • Linux中 MySQL 授權(quán)遠(yuǎn)程連接的方法步驟

    Linux中 MySQL 授權(quán)遠(yuǎn)程連接的方法步驟

    如果需要遠(yuǎn)程連接 Linux 系統(tǒng)上的 MySQL 時(shí),必須為其 IP 和 具體用戶 進(jìn)行 授權(quán),本篇文章主要介紹了Linux中 MySQL 授權(quán)遠(yuǎn)程連接的方法步驟,感興趣的小伙伴們可以參考一下
    2018-10-10

最新評(píng)論

钟祥市| 洪泽县| 陵川县| 共和县| 云浮市| 康平县| 乐昌市| 女性| 贵南县| 浏阳市| 沂源县| 海兴县| 蒙自县| 格尔木市| 靖远县| 同心县| 盐山县| 南漳县| 清水县| 肥东县| 黄平县| 微博| 玉山县| 都匀市| 大连市| 信阳市| 安图县| 新建县| 丁青县| 漳浦县| 老河口市| 榆社县| 新乡市| 文安县| 内丘县| 吴川市| 景谷| 宜春市| 舞阳县| 玉环县| 宁海县|