mysql 默认事务隔离级别_上个厕所的功夫,搞懂MySQL事务隔离级别
“隔離級別” 出于MySQL四大特性(ACID)中的“I”,也就是隔離性。目的是實現數據、事務一致性“C”。
MySQL在多線程并發場景下,可能會出現臟讀(dirty read)、不可重復讀(non-repeatable read)、幻讀(phantom read)這類并發問題,為了解決這些問題,引申出“隔離級別”的概念。
談隔離級別之前,你首先需要知道,隔離得越嚴實,效率就會越低。隔離就像是生活中那一件件鎖事兒,枷鎖越多,活得越累。在很多時候,我們都要在二者之間尋找一個平衡點。
測試數據準備
測試表是一些好朋友客串一下,請原諒我的不要臉【/偷笑】。(建表語句在文章末尾)
mysql> select * from department;+----+-----------+-----+-----+------------+------+-----------+| ID | NAME | SEX | AGE | CLASS | PAY | HOBBY |+----+-----------+-----+-----+------------+------+-----------+| 1 | 陳哈哈 | 男 | 25 | 技術1部 | 3000 | 摸魚 || 2 | 扈亞鵬 | 男 | 25 | 技術1部 | 4000 | 美食 || 3 | 劉曉莉 | 女 | 24 | 技術1部 | 4000 | 摸魚 || 5 | 徐立楠 | 女 | 24 | 技術1部 | 4000 | 閱讀 || 6 | 顧昊 | 男 | 25 | 技術1部 | 4000 | 摸魚 || 7 | 陳子凝 | 女 | 25 | 技術1部 | 5000 | 看電影 || 14 | 朱志鵬 | 男 | 25 | 技術1部 | 5000 | 看小說 || 19 | 李昂 | 男 | 27 | 技術1部 | 7000 | 看片兒 |+----+-----------+-----+-----+------------+------+-----------+8 rows in set (0.00 sec)為了更好理解下文,這里先給出個業務場景:
老板:原來陳哈哈是我失散多年的大侄子!財務,給 “陳哈哈” 的工資漲 “10000” 大洋。
陳哈哈:謝謝老叔!MUA~
并發場景下事務存在的數據問題
下面我們介紹一下臟讀(dirty read)、不可重復讀(non-repeatable read)、幻讀(phantom read)這三類并發問題,以及每種問題出現的原理及場景。
1. 臟讀(針對的是未提交讀數據)
事務A修改了數據,但未提交,而事務B查詢了事務A修改過卻沒有提交的數據,這就是臟讀,因為事務A可能會回滾。
場景:老板(老叔)大喊了一嗓子,但沒有指定哪個財務改。財務A大姐和財務B大哥都聽到了,但他倆不知道由誰來改,就分別進行了下方流程操作:
就這樣,因為 “臟讀”就導致我每個月少1萬大洋?
2. 不可重復讀(針對其他提交前后,讀取數據本身的對比)
事務A 先 查詢了工資金額,是3000塊錢,未提交 。事務B在事務A查詢完之后,修改了工資金額,變成了13000, 在事務A前提交了;如果此時事務A再查詢一次數據,就會發現錢跟上一次查詢不一致,是13000,而不是3000。這就是不可重復讀。強調事務A對要操作的數據被別人修改了,但在不知情的情況下拿去做之前的用途。
場景同上:老板嗷一嗓子,但沒有指定哪個財務改。財務A大姐和財務B大哥都聽到了,但他倆不知道由誰來改,就分別進行了下方流程操作:
對于不可重復讀,說簡單點就是同一個事物內,查到的結果都不一致,就失去了MySQL的“一致性”,這是很嚴重的錯誤。你想,如果財務大姐沒有二次確認,而是直接以第一次查詢為準,又給我加了1萬怎么辦?想想還有點小激動呢
3. 幻讀(針對其他提交前后,讀取數據條數的對比)
幻讀是指在同一個事務中,存在前后兩次查詢同一個范圍的數據,但是第二次查詢卻看到了第一次查詢沒看到的行,一般情況下只新增。
事務A先修改了某個表的所有記錄的狀態字段為已處理,未提交;事務B也在此時新增了一條未處理的記錄,并提交了;事務A隨后查詢記錄,卻發現有一條記錄是未處理的,很是詫異,剛剛不是全部修改為已處理嘛,以為出現了幻覺,這就是幻讀。
場景:老板每個月審批一次漲薪(審批表:shenpiTable),這時財務剛剛把我的工資申請提交了,老板正好在審批。一鍵審批通過后,突然看到了一條新的“未審批”記錄(新增的),還是大侄子陳哈哈的。
老板:有幻覺?有BUG!!等等,我如果假裝看不到這月是不是就省了1萬塊大洋?
陳哈哈:???
- 臟讀說的是事務知道了自己本不應該知道的東西,強調的動作是查詢,我看到了自己不該看的東西 ;
- 不可重復讀強調的是一個人查的時候,其他人卻可以增刪改, 但我卻不知道數據被改了,還拿去做了之前的用途;
- 幻讀強調的是我修改了數據,等我要查的時候,卻發現有我沒有修改的記錄,為什么,因為有其他人插了一條新的。
隔離級別概述
為了解決上述問題,MySQL制定了四種不同的“隔離級別”,包括:讀未提交(read uncommitted)、讀提交(read committed)、可重復讀(repeatable read)和串行化(serializable )。
實例分析
(場景再現)老板:原來陳哈哈又雙叒(ruò)叕(zhuó)是我失散多年的大侄子!財務,把 “陳哈哈” 的工資漲 “10000” !
以下表中的兩個事務為例,看看在不同隔離級別下,分別會出現什么結果。能否避免上述問題呢?
- 讀未提交(RU)
在RU隔離級別下,事務A 在T5時刻,就可以提前讀到未提交的事務B 結果。
- 讀提交(RC)
讀提交又叫讀已提交,在RC隔離級別下,事務A 需要在 事務B commit提交后,才能看到事務B 修改的結果。所以在T5時刻,事務A 查到的陳哈哈的工資是 3000。
- 可重復讀(RR)
可重復讀是MySQL默認的隔離級別,在RR級別下,對于所有進行中(begin - commit)的事務,比如事務A,無論執行多少次SELECT(查詢表 department ),只能看到的是同一張 department 表的結果視圖(ReadView),該視圖(ReadView)是在本事務啟動(begin)時生成的,在事務A 結束(commit)后釋放。該隔離級別會保證單事務內查看視圖的一致性,稱為“可重復讀”。
- 串行(xíng)化(S)
串行化隔離級別不支持并發事務,由于事務A 早于事務B,事務A執行SELECT時,就給 department 表加了鎖,事務B 需要等事務A 結束后才能執行,因此T5、T7時刻是 3000,T8時刻事務A提交,事務B釋放鎖并執行,最后T9時刻查到我的工資是 13000。
原理描述
在實現上,數據庫里面會創建一個視圖,訪問的時候以視圖的邏輯結果為準。在MySQL默認的隔離級別“可重復讀”隔離級別下,這個視圖是在事務啟動時創建的,整個事務存在期間都用這個視圖。在“讀提交”隔離級別下,這個視圖是在每個 SQL 語句開始執行的時候創建的。這里需要注意的是,“讀未提交”隔離級別下直接返回記錄上的最新值,沒有視圖概念;而“串行化”隔離級別下直接用加鎖的方式來避免并行訪問。
我們可以看到在不同的隔離級別下,數據庫行為是有所不同的。Oracle 數據庫的默認隔離級別其實就是“讀提交”,因此對于一些從 Oracle 遷移到 MySQL 的應用,為保證數據庫隔離級別的一致,你一定要記得將 MySQL 的隔離級別設置為“讀提交”。
配置的方式是,將啟動參數 transaction-isolation 的值設置成 READ-COMMITTED。你可以用 show variables 來查看當前的值。
mysql> show variables like 'transaction_isolation';?+-----------------------+----------------+| Variable_name | Value |+-----------------------+----------------+| transaction_isolation | READ-COMMITTED |+-----------------------+----------------+總結來說,存在即合理,每種隔離級別都有自己的使用場景,你要根據自己的業務情況來定。我想你可能會問那什么時候需要“可重復讀”的場景呢?我們來看一個數據校對邏輯的案例。
假設你在管理一個個人銀行賬戶表。一個表存了每個月月底的余額,一個表存了賬單明細。這時候你要做數據校對,也就是判斷上個月的余額和當前余額的差額,是否與本月的賬單明細一致。你一定希望在校對過程中,即使有用戶發生了一筆新的交易,也不影響你的校對結果。
這時候使用“可重復讀”隔離級別就很方便。事務啟動時的視圖可以認為是靜態的,不受其他事務更新的影響。
四種隔離級別的問題解決情況
測試建表語句
-- 建表語句DROP TABLE IF EXISTS `department`;CREATE TABLE `department` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `NAME` varchar(30) CHARACTER SET utf8mb4 NOT NULL, `SEX` char(2) NOT NULL, `AGE` int(11) NOT NULL, `CLASS` varchar(10) NOT NULL, `PAY` int(11) NOT NULL, `HOBBY` varchar(100) DEFAULT NULL, PRIMARY KEY (`ID`)) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;INSERT INTO `department` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `PAY`, `HOBBY`) VALUES ('1', '陳哈哈', '男', '25', '技術1部', '3000', '摸魚');INSERT INTO `department` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `PAY`, `HOBBY`) VALUES ('2', '扈亞鵬', '男', '25', '技術1部', '4000', '美食');INSERT INTO `department` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `PAY`, `HOBBY`) VALUES ('3', '劉曉莉', '女', '24', '技術1部', '4000', '摸魚');INSERT INTO `department` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `PAY`, `HOBBY`) VALUES ('5', '徐立楠', '女', '24', '技術1部', '4000', '閱讀');INSERT INTO `department` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `PAY`, `HOBBY`) VALUES ('6', '顧昊', '男', '25', '技術1部', '4000', '摸魚');INSERT INTO `department` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `PAY`, `HOBBY`) VALUES ('7', '陳子凝', '女', '25', '技術1部', '5000', '看電影');INSERT INTO `department` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `PAY`, `HOBBY`) VALUES ('14', '朱志鵬', '男', '25', '技術1部', '5000', '看小說');INSERT INTO `department` (`ID`, `NAME`, `SEX`, `AGE`, `CLASS`, `PAY`, `HOBBY`) VALUES ('19', '李昂', '男', '27', '技術1部', '7000', '看片兒');總結
以上就是對MySQL中三種并發數據問題以及四種隔離級別的介紹,有些朋友會問,那這幾種隔離級別解決上述三種問題的原理是什么呢?可重復讀RR是如何解決幻讀問題的?
后續解答...
作者:_陳哈哈
原文鏈接:https://blog.csdn.net/qq_39390545/article/details/107343711
總結
以上是生活随笔為你收集整理的mysql 默认事务隔离级别_上个厕所的功夫,搞懂MySQL事务隔离级别的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C++基础15-类和对象之多态
- 下一篇: 基础功能-tensorflow使用gpu