MySQL事務(4種事務隔離級別、臟寫、臟讀、不可重復讀、幻讀、當前讀、快照讀、MVCC、事務指標監控)

聲明測試表,供文章案例使用

CREATE TABLE `cs` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `num` int(10) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

事務的分類

  • 顯示事務:
    • read write:讀寫事務,默認模式,表示當前事務可以讀寫數據。
    • read only:只讀事務,很少用,表示當前事務不能修改數據。
    • with consistent snapshot:一致性快照,在數據庫事務中確保事務在執行過程中能看到一個事務開始時的一致數據庫狀態,避免被其他并發操作影響。
  • 隱式事務:不需要顯示聲明事務相關語句,autocommit是開啟狀態(默認值),每條DML和DDL的SQL語句都是一個獨立的事務。

MySQL事務的4個特性:

  • 原子性(Atomicity):當前事務中的執行結果,要么全部執行成功,要么全部執行失敗。
  • 一致性(Consistency):事務執行前后,數據庫從一個合法(指符合業務預期)狀態轉換成另一個一合法狀態。
  • 隔離性(Isolation):多個事務可以并發執行,各個事務之間的操作互相隔離互不干擾。
  • 持久性(Durability):事務提交,會持久化到磁盤中。

自動提交

自動提交(auto commit),指的是SQL語句執行完畢后自動將數據持久化到磁盤(刷盤)中。
顯式的聲明事務,或者聲明set autocommit = 0;都可以關閉自動提交。

savepoint

  • 俗稱保存點,是用于實現部分事務回滾的一種機制,需要確定從哪里開始回滾,就需要savepoint的標識來定位。
  • 回滾范圍:從保存點開始到事務最后一條SQL,都會被回滾。
  • 適用場景:用于復雜的業務邏輯中,給出靈活可控的后悔藥,降低事務回滾影響范圍。
  • 注意:rollback to之后,不代表事務流程走完,還需要再次commit提交其它未回滾的事務。
  • 用法:
    savepoint 保存點名:創建一個 Savepoint,并為其指定一個名稱。
    rollbackto savepoint 保存點名;:將事務回滾到指定的 Savepoint。
    release savepoint 保存點名;:釋放指定的 Savepoint。
    示例:
start transaction;
insert into cs(num) values(1);
savepoint insert_1;
insert into cs(num) values(2);
savepoint insert_2;
insert into cs(num) values(3);
savepoint insert_3;
insert into cs(num) values(4);
savepoint insert_4;
rollback to insert_2;
commit;
發現1,2數據被插入。

事務的隱式提交

在上一個事務沒提交或回滾時,運行下一個事務,則上一個事務自動提交。

start transaction;
insert into cs(num) values(1);
insert into cs(num) values(2);
start transaction;
insert into cs(num) values(3);
insert into cs(num) values(4);
commit;
成功插入1,2,3,4。

4種隔離級別

  • 讀未提交(Read Uncommitted):最低級別的隔離,事務中的修改即使未提交也能被其他事務看到,可能導致臟讀、不可重復讀和幻讀問題。
  • 讀已提交(Read Committed):保證一個事務提交后對其他事務可見,避免了臟讀,但可能會導致不可重復讀和幻讀問題。
  • 可重復讀(Repeatable Read):保證在同一事務內多次讀取數據時,數據保持一致,避免了不可重復讀問題,但仍可能出現幻讀。
  • 串行化(Serializable):最高級別的隔離,通過對讀取的數據添加共享鎖或排他鎖來確保事務之間的隔離性,避免了臟讀、不可重復讀和幻讀問題,但可能會影響并發性能。

表格從上到下,越來越高可用,但是性能越來越低。

隔離級別 是否解決臟讀 是否解決不可重復讀 是否解決幻讀 是否加鎖
讀未提交
讀已提交
可重復讀
串行化

查看或設置MySQL隔離級別

  • 查看:select @@transaction_isolation;或者show variables like 'transaction_isolation;'
  • 設置:set session transaction_isolation = 'read-uncommitted/read-committed/repeatable-read/serializable';
    注意隔離級別是回話級別的,所以無法set glboal。

MySQL會發生什么讀?

因為mysql默認隔離級別是可重復讀(Repeatable Read),所以只會發生幻讀情況,臟讀和可重復度不會發生,除非改事務隔離級別。

臟寫(不允許發生)

  • 簡介:一個事務修改某些數據時,另一個事務在未提交的情況下也修改了這些數據,引起的導致數據的不一致性。
  • 危害:造成數據在并發情況下嚴重不一致。
  • 演示:試不出來,臟寫這么嚴重的bug,是不允許發生的情況。

臟讀(讀未提交隔離級別會發生)

  • 簡介:一個事務尚未commit(提交,刷盤,持久化),卻讀取了事務修改后的值,引起數據讀取不準確的情況。
  • 危害:事務還未提交就被讀取了,該事務成功提交還好,要是回滾了,會造成讀取數據不一致的問題。
  • 演示:因為臟讀是讀未提交(Read Uncommitted)才會發生的情況,所以要降低MySQL的隔離級別。
步驟 會話A 會話B 備注
1 set session transaction_isolation = 'read-committed'; set session transaction_isolation = 'read-committed'; 設置事務的隔離級別為讀未提交
2 select @@transaction_isolation; select @@transaction_isolation; 檢查隔離級別是否設置成功
3 select num from cs where id = 20; #20 start transaction;
update cs set num = 20 where id = 40;
會話A num的初始值為20
4 select num from cs where id = 20; #40 / 會話B并未commit,此時會話A中num的值為40,發生臟讀現象
5 / rollback 結束本次事務
6 select num from cs where id = 20; #20 / num恢復為20

不可重復讀(讀未提交、讀已提交隔離級別會發生)

  • 簡介:在事務A中讀取某些數據,然后在事務B中修改這些數據,此時事務A讀取這些數據還未發生變化,但是事務B提交后,并在事務A在未結束事務的前提下,那些數據發生了變化,不可重復讀不是禁止讀動作,而是重復讀數據不一致。
    一句話概括,在同一個事務中,受其它事務提交的影響,讀取同一數據兩次得到的結果不一致的現象。
  • 危害:破壞了事務內數據的準確性,例如事務內的SQL有自增自減的邏輯,如果事務內的初始值受其他事物提交從而發生變化,那么這是個巨大的問題。
  • 演示:因為不可重復讀是讀已提交(Read Committed)才會發生的情況,所以要降低MySQL的隔離級別。
步驟 會話A 會話B 備注
1 set session transaction_isolation = 'read-uncommitted'; set session transaction_isolation = 'read-uncommitted'; 設置事務隔離級別為讀已經提交
2 select @@transaction_isolation; select @@transaction_isolation; 檢查隔離級別是否設置成功
3 start transaction; start transaction; 雙方開啟事務
4 select num from cs where id = 20; #20 select num from cs where id = 20; #20 兩個會話中num的值為20
5 update cs set num = 40 where id = 20; select num from cs where id = 20; #20 會話A將數據更新為40,此時會話B查詢的值仍為20
6 commit select num from cs where id = 40; #40 會話A提交事務,會話B仍在事務中,但是得到的值變成了40,發生了不可重復讀
7 / commit 結束事務

幻讀(讀未提交、讀已提交、可重復讀隔離級別會發生)

  • 簡介:同一個事務里前后查詢兩次相同范圍的數據,后一次查詢查詢到了前一次看不到的東西,就好像出現了"幻影"一樣。(注意,如果把會話B的insert改為delete導致的數據減少,不算幻讀,算不可重復讀)。
  • 危害:沒有充分的做好數據隔離,數據一致性存在問題。
  • 演示:mysql 的默認隔離級別為REPEATABLE-READ,所以大概率不用調整隔離級別。
步驟 會話A 會話B 備注
1 select @@transaction_isolation; select @@transaction_isolation; 檢查隔離級別是否是REPEATABLE-READ
2 start transaction; start transaction; 雙方開啟事務
3 select * from cs; select * from cs; 兩個事務查看,都只有id為20的一條數據
4 insert into cs (id,num) values(21,21); select * from cs; 會話B查詢,仍舊只有id為20的一條數據
5 commit / 會話A提交事務
6 / select * from cs; 即使會話A提交了事務,會話B查詢仍舊無法搜索到會話A插入的數據,起始這一步已經幻讀了,但是mysql不表明是幻讀,所以到第7步測試
7 / insert into cs (id,num) values(21,21); 因為會話B select查不到id為21的數據,所以插入id相同的數據,但是報錯1062 - Duplicate entry '21' for key 'PRIMARY'
8 / rollback; 回滾以結束事務流程

如何解決幻讀?

  • 或者使用串行化的隔離級別。在串行化隔離級別下,也會隱式的添加行(X)鎖。
  • 添加間隙鎖,可以避免幻讀。
  • mysql 的默認隔離級別為REPEATABLE-READ,又稱為RR,通過MVCC的機制,如果對數據進行快照讀,正因為讀取的不一定第最新的數據,所以可以防止幻讀(注意不是解決幻讀),如果是當前讀(最近數據),那么仍舊會發生幻讀現象。

當前讀

當前讀讀的就是數據最新的記錄,需要保證當前讀的數據不能被修改,修改了就不是最新的記錄了(臟寫),因此需要加鎖,select for update、select lock in share mode以及DML(insert、update、delete)獲取的數據都是當前讀的數據。

快照讀

快照讀顧名思義,讀取的就是由MVCC Read View控制的undo log的數據,不加鎖,所以是讀取是非阻塞的。不加鎖的select都屬于快照讀。如果當前事務的隔離級別是串行化,那么快照讀也變成了當前讀。
舉個例子:常用的navicat,查看一個表,事務提交前的insert或update語句,表格內仍舊顯示的原數據,則用的快照讀。

MVCC

MVCC(Multi-Version Concurrency Control)是 MySQL 中一種實現事務隔離的機制,用于處理數據庫事務并發訪問時可能出現的讀寫沖突。事務的四種隔離級別,就是通過MVCC機制提供的底層支撐。
MVCC三板斧:隱藏字段、Undo log(存放歷史版本)、Read view(版本控制)

MVCC解決的是讀已提交和可重復讀級別的并發控制。
因為讀未提交,就算事務未提交,可以直接讀取最新的數據(臟讀),相當于當前讀,那就不分快照讀和當前讀了。
串行化的隔離級別,強制事務串行執行,也不存在快照讀和當前讀的區分,因為讀取的都是事務執行過后的最新數據。

事務各項指標監控

查看 InnoDB 存儲引擎中當前活動的事務信息。

SELECT * FROM information_schema.innodb_trx;

trx_id                         事務的唯一標識符。
trx_state                      事務的狀態,如 RUNNING、LOCK WAIT、ROLLING BACK 等。
trx_started                    事務啟動的時間。
trx_requested_lock_id          請求的鎖的標識符。
trx_wait_started               等待鎖的開始時間。
trx_weight                     事務的權重,用于死鎖檢測。
trx_mysql_thread_id            MySQL 線程 ID。
trx_query                      與事務相關的 SQL 查詢語句。
trx_operation_state            事務內部操作的狀態。
trx_tables_in_use              事務使用的表的數量。
trx_tables_locked              事務鎖定的表的數量。
trx_lock_structs               事務內部使用的鎖結構數量。
trx_lock_memory_bytes          用于事務鎖定的內存字節數。
trx_rows_locked                事務鎖定的行數。
trx_rows_modified              事務修改的行數。
trx_concurrency_tickets        用于事務并發控制的票數。
trx_isolation_level            事務的隔離級別。
trx_unique_checks              是否啟用了唯一性檢查。
trx_foreign_key_checks         是否啟用了外鍵約束檢查。
trx_last_foreign_key_error     最后一個外鍵錯誤信息。
trx_adaptive_hash_latched      是否適應性哈希被鎖定。
trx_adaptive_hash_timeout      適應性哈希鎖定超時次數。
trx_is_foreign_key_with_check  是否用于外鍵約束檢查。
trx_is_foreign_key             是否用于外鍵約束。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,619評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,155評論 3 425
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,635評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,539評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,255評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,646評論 1 326
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,655評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,838評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,399評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,146評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,338評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,893評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,565評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,983評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,257評論 1 292
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,059評論 3 397
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,296評論 2 376

推薦閱讀更多精彩內容