簡書不維護了,歡迎關注我的知乎:波羅學的個人主頁
本篇文章的重點在于總結MYSQL事務横媚。
事務是什么?
事務簡言之就是一組SQL執(zhí)行要么全部成功底循,要么全部失敗。MYSQL的事務在存儲引擎層實現(xiàn)廊驼。
事務都有ACID特性:
- 原子性(Atomicity):一個事務必須被視為一個不可分割的單元;
- 一致性(Consistency):數(shù)據庫總是從一種狀態(tài)切換到另一種狀態(tài)惋砂;
- 隔離性(Isolation):通常來說妒挎,事務在提交前對于其他事務不可見;
- 持久性(Durablity):一旦事務提交西饵,所做修改永久保存數(shù)據庫酝掩;
事務最常用的例子就是銀行轉賬。假設polo需給lynn轉賬1000元眷柔,如下步驟:
- 確認polo賬戶余額高于1000元期虾;
- 從polo的賬戶余額減去1000元原朝;
- 將lynn的賬戶余額增加1000元;
SQL語句如下:
mysql> BEGIN;
mysql> SELECT balance FROM bank_account WHERE uid=10001;
mysql> UPDATE bank_account SET balance=balance-1000 WHERE uid=10001;
mysql> UPDATE bank_account SET balance=balance+1000 WHERE uid=10002;
mysql> COMMIT;
注:mysql啟動事務可使用BEGIN或者START TRANSACTION镶苞;
上述三個步驟執(zhí)行在一個事務中就能夠保證數(shù)據的完整性喳坠,要么全部成功,要么全部失敗宾尚。
MYSQL提供兩種事務型引擎:Innodb和NDBCluster。默認采用自動提交模式谢澈,執(zhí)行一條語句自動COMMIT煌贴。通過AUTOCOMMIT變量可啟用或者禁用自動提交模式:
mysql> SHOW VARIABLES LIKE "AUTOCOMMIT";
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| autocommit | ON |
+---------------+-------+
1 row in set (0.00 sec)
mysql> SET AUTOCOMMIT=1
AUTOCOMMIT=1表示開啟默認提交,0表示關閉默認提交需要手動提交锥忿。
事務隔離級別
事務隔離性的解釋:通常情況下牛郑,事務在提交之前對于其他事務不可見。
數(shù)據庫有四種隔離級別敬鬓,當然MYSQL也是如此淹朋。
本人理解,隔離級別就是決定一個事務的修改另一個事務什么情況下能看到钉答。
書本解釋础芍,每種級別都規(guī)定了一個事務中所做修改,哪些在事務內和事務間是可見的数尿。
區(qū)別在于是否存在事務內可見性的規(guī)定仑性。我在四個級別似乎沒有看到
下面開始說明MYSQL的四種隔離級別,先準備一張學生表:
mysql> CREATE TABLE `student` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL DEFAULT '',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 |
只有id(主鍵自增)與name字段
-
READ UNCOMMITTED(未提交讀)
事務中修改沒有提交對其他事務也是可見的右蹦,俗稱臟讀诊杆。非常不建議使用。實例演示
客戶端A和B設置隔離級別為未提交讀mysql> SET SESSION TX_ISOLATION='READ-UNCOMMITTED';
客戶端A與B開啟事務并查詢student
mysql> BEGIN何陆; mysql> SELECT * FROM student; Empty set (0.00 sec)
客戶端A和B都是空數(shù)據
客服端B插入一條新的數(shù)據
mysql> INSERT INTO student(name) VALUES("polo"); Query OK, 1 row affected (0.00 sec)
此時事務未提交晨汹,客服端A查看student表
$ SELECT * FROM student; mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | polo | +----+------+ 1 row in set (0.00 sec)
<font color=red>客戶端A看到B未提交的修改</font>
客戶端B執(zhí)行回滾操作
mysql> ROLLBACK
成功之后,客戶端A查看student表
mysql> SELECT * FROM student; Empty set (0.00 sec)
<font color=red>客戶端A查看數(shù)據為空</font>
以上可以看出未提交讀隔離級別的危險性贷盲,對于一個沒有提交事務所做修改對另一個事務是可見狀態(tài)淘这,容易造成臟讀。非特殊情況不得使用此級別
-
READ COMMITTED(提交讀)
多數(shù)數(shù)據庫系統(tǒng)默認為此級別(MYSQL不是)巩剖。已提交讀級別即為一個事務只能已提交事務所做的修改慨灭,也就解決了未提交讀的問題,即臟讀的問題球及。實例演示
客戶端A和B設置隔離級別為已提交讀mysql> SET SESSION TX_ISOLATION='READ-COMMITTED';
客戶端A與B開啟事務并查詢student
mysql> BEGIN; mysql> SELECT * FROM student; Empty set (0.00 sec)
客戶端A和B都為空
客服端B插入一條新的數(shù)據氧骤,不提交mysql> INSERT INTO student (name) VALUES('polo');
客戶端A查看student
mysql> SELECT * FROM student; Empty set (0.00 sec)
<font color=red>注意這里與上面不同了,在客戶端B沒有提交事務情況下無數(shù)據</font>
下面客戶端B提交事務mysql> COMMIT;
客戶端A再次查看student表吃引。
mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | polo | +----+------+ 1 row in set (0.00 sec)
成功讀取到客戶
從上面的示例可以看出筹陵,提交讀沒有了未提交讀的問題刽锤,但我們可以看到在客戶端A的一個事務中執(zhí)行兩次同樣的SELECT語句得到不同結果,因此已提交讀又被稱為不可重復讀朦佩。同樣篩選條件可能得到不同的結果并思。
-
REPEATABLE READ(可重復讀)
如其名也,解決已提交讀不可重復讀取的問題语稠。示例演示
客戶端A和B設置隔離級別為可重復讀mysql> SET SESSION tx_isolation='REPEATABLE-READ'
客戶端A與B開啟事務并查看
mysql> BEGIN; mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | polo | +----+------+ 1 rows in set (0.00 sec)
客服端B更新polo為adam宋彼,并提交事務
mysql> UPDATE student SET name='adam' WHERE id=1; mysql> COMMIT
客戶端A查看student表
mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | polo | +----+------+ 1 rows in set (0.00 sec)
<font color=red>注意客戶端A查看數(shù)據未變,沒有不可重復讀問題</font>
客戶端A提交事務仙畦,并查看student表
mysql> COMMIT; mysql> SELECT * FROM student; +----+------+ | id | name | +----+------+ | 1 | polo | +----+------+ 1 rows in set (0.00 sec)
上面實例可知输涕,可重復讀兩次讀取內容一樣。數(shù)據庫這級別并沒有解決幻讀的問題慨畸。但是MYSQL在可重復讀基礎上增加了MVCC機制解決了此問題莱坎,實例無法演示幻讀效果。
那什么是幻讀寸士?首先檐什,可重復讀鎖定范圍為當前查詢到的內容,如執(zhí)行
mysql> SELECT * FROM student WHERE id>=1
鎖定的即id>=1查到的行弱卡,為行級鎖乃正。如另一事務執(zhí)行并默認提交以下語句
mysql> INSERT INTO student (name) VALUES ('stephen');
新增的這行并沒有被鎖定,此時讀取student
mysql> SELECT * FROM student WHERE id>=1; +----+---------+ | id | name | +----+---------+ | 1 | polo | | 2 | stephen | +----+---------+ 2 rows in set (0.00 sec)
便出現(xiàn)了幻讀
除了使用MYSQL的MVCC機制婶博,還可以使用可串行化隔離級別解決此問題烫葬。
-
SEAIALIZABLE(可串行化)
可串行化是最高隔離級別,強制事務串行執(zhí)行凡蜻。執(zhí)行串行了也就解決了一切的問題搭综,這個級別只有在對數(shù)據一致性要求非常嚴格且沒用并發(fā)的情況下使用實例演示
客戶端A和B設置隔離級別為可串行化mysql> SET SESSION tx_isolation='SERIALIZABLE';
客戶端A執(zhí)行查詢
mysql> SELECT * FROM student WHERE id<4; +----+---------+ | id | name | +----+---------+ | 1 | polo | | 2 | stephen | +----+---------+ 2 rows in set (0.00 sec)
客戶端B執(zhí)行新增
mysql> INSERT INTO student (name) VALUES('yunteng');
好的!效果出現(xiàn)了划栓,此時我們會發(fā)現(xiàn)INSERT語句被阻塞執(zhí)行兑巾,原因就是A執(zhí)行了查詢表student同時滿足id<4,已被鎖定忠荞。如果查詢表student條件為id<3蒋歌,則新增語句可正常執(zhí)行。
隔離級別對照圖
隔離級別 | 臟讀 | 不可重復讀 | 幻讀 | 加鎖讀 |
---|---|---|---|---|
未提交讀 | 是 | 是 | 是 | 否 |
提交讀 | 否 | 是 | 是 | 否 |
可重復讀 | 否 | 否 | 是 | 否 |
未提交讀 | 否 | 否 | 否 | 是 |
好了委煤,關于事務的隔離級別就說這么多堂油,希望自己的理解沒有錯誤。