這篇文章給大家介紹中SET TRANSACTION會(huì )不會(huì )影響事務(wù),內容非常詳細,感興趣的小伙伴們可以參考借鑒,希望對大家能有所幫助。
MySQL支持SQL:1992標準中的所有事務(wù)隔離級別,使用SET TRANSACTION來(lái)設置不同的事務(wù)隔離級別或訪(fǎng)問(wèn)模式。
我們都知道,MySQL的內置引擎中只有InnoDB、NDB支持事務(wù),而又以InnoDB引擎對于事務(wù)的支持最全面也使用最廣泛,所以本文的討論都是基于InnoDB引擎,實(shí)驗中用的表都是基于InnoDB的表。
MySQL中可以使用SET TRANSACTION來(lái)影響事務(wù)特性,此語(yǔ)句可以指定一個(gè)或多個(gè)由逗號分隔的特征值列表,每個(gè)特征值設置事務(wù)隔離級別或訪(fǎng)問(wèn)模式。此語(yǔ)句在MySQL 5.7中的完整語(yǔ)法
SET [GLOBAL | SESSION] TRANSACTION transaction_characteristic [, transaction_characteristic] ... transaction_characteristic: { ISOLATION LEVEL level | access_mode }level: { REPEATABLE READ | READ COMMITTED | READ UNCOMMITTED | SERIALIZABLE} access_mode: { READ WRITE | READ ONLY}
語(yǔ)法很簡(jiǎn)單清晰,這里有幾個(gè)關(guān)鍵概念需要理解清楚。
事務(wù)隔離是數據庫的基礎能力,ACID中的I指的就是事務(wù)隔離,通俗點(diǎn)講就是多個(gè)用戶(hù)并發(fā)訪(fǎng)問(wèn)數據庫時(shí),數據庫為每一個(gè)用戶(hù)開(kāi)啟的事務(wù),不能被其他事務(wù)的操作數據所干擾,多個(gè)并發(fā)事務(wù)之間要相互隔離。
那么到底如何做才算是相互隔離呢?SQL:1992標準規定了四種事務(wù)隔離級別:READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ和SERIALIZABLE。
InnoDB對四種隔離級別都支持,默認級別是REPEATABLE READ。
root@database-one 07:43: [(none)]> select @@tx_isolation; +-----------------+| @@tx_isolation | +-----------------+| REPEATABLE-READ | +-----------------+1 row in set (0.00 sec)
新建會(huì )話(huà)進(jìn)行驗證,會(huì )話(huà)的默認隔離級別確實(shí)REPEATABLE-READ。
InnoDB是靠不同的鎖策略實(shí)現每個(gè)事務(wù)隔離級別,隔離級別越高付出的鎖成本也就會(huì )越高。我們通過(guò)例子來(lái)看看不同級別的區別。
root@database-one 08:38: [gftest]> create table testtx(name varchar(10),money decimal(10,2)) engine=innodb; Query OK, 0 rows affected (0.12 sec) root@database-one 08:42: [gftest]> insert into testtx values('A',6000),('B',8000),('C',9000); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 root@database-one 08:43: [gftest]> select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
上面創(chuàng )建了表testtx,并插入了3條數據,表示A有6000元,B有8000元,C有9000元。
REPEATABLE READ,同一事務(wù)內的consistent reads讀取由第一次讀取建立的快照。這意味著(zhù),如果在同一事務(wù)中發(fā)出多個(gè)普通(非鎖定)SELECT語(yǔ)句,則這些SELECT語(yǔ)句查到的數據保持一致。
創(chuàng )建會(huì )話(huà)1,關(guān)閉MySQL默認的事務(wù)自動(dòng)提交模式(相關(guān)知識可以參考 MySQL中的事務(wù)控制語(yǔ)句)。
root@database-one 08:58: [(none)]> prompt \u@database-one \R:\m:\s [\d] session1> PROMPT set to '\u@database-one \R:\m:\s [\d] session1>'root@database-one 08:58:41 [(none)] session1>use gftest; Database changed root@database-one 08:58:55 [gftest] session1>SET autocommit=0; Query OK, 0 rows affected (0.00 sec) root@database-one 08:59:21 [gftest] session1>show variables like 'autocommit'; +---------------+-------+| Variable_name | Value | +---------------+-------+| autocommit | OFF | +---------------+-------+1 row in set (0.02 sec) root@database-one 08:59:36 [gftest] session1>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
創(chuàng )建會(huì )話(huà)2,關(guān)閉MySQL默認的事務(wù)自動(dòng)提交模式(相關(guān)知識可以參考 MySQL中的事務(wù)控制語(yǔ)句)。
root@database-one 09:01: [(none)]> prompt \u@database-one \R:\m:\s [\d] session2> PROMPT set to '\u@database-one \R:\m:\s [\d] session2>'root@database-one 09:02:13 [(none)] session2>use gftest; Database changed root@database-one 09:02:24 [gftest] session2>SET autocommit=0; Query OK, 0 rows affected (0.00 sec) root@database-one 09:02:30 [gftest] session2>show variables like 'autocommit'; +---------------+-------+| Variable_name | Value | +---------------+-------+| autocommit | OFF | +---------------+-------+1 row in set (0.00 sec) root@database-one 09:02:37 [gftest] session2>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
創(chuàng )建會(huì )話(huà)3,關(guān)閉MySQL默認的事務(wù)自動(dòng)提交模式(相關(guān)知識可以參考 MySQL中的事務(wù)控制語(yǔ)句)。
root@database-one 09:03: [(none)]> prompt \u@database-one \R:\m:\s [\d] session3> PROMPT set to '\u@database-one \R:\m:\s [\d] session3>'root@database-one 09:03:44 [(none)] session3>use gftest; Database changed root@database-one 09:03:47 [gftest] session3>SET autocommit=0; Query OK, 0 rows affected (0.00 sec) root@database-one 09:03:56 [gftest] session3>show variables like 'autocommit'; +---------------+-------+| Variable_name | Value | +---------------+-------+| autocommit | OFF | +---------------+-------+1 row in set (0.01 sec) root@database-one 09:04:04 [gftest] session3>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
A給B轉100元。在session1中模擬。
root@database-one 09:06:03 [gftest] session1>update testtx set money=money-100 where name='A'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 root@database-one 09:07:34 [gftest] session1>update testtx set money=money+100 where name='B'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 root@database-one 09:07:58 [gftest] session1>select * from testtx; +------+---------+| name | money | +------+---------+| A | 5900.00 | | B | 8100.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
session1看到了金額進(jìn)行了變化,但還未進(jìn)行提交。
此時(shí),分別去session2、session3進(jìn)行查詢(xún)。
root@database-one 09:02:45 [gftest] session2> root@database-one 09:12:23 [gftest] session2>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
root@database-one 09:04:10 [gftest] session3> root@database-one 09:14:12 [gftest] session3>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
session2、session3均未看到金額變化。
A對轉賬進(jìn)行確認,即提交。
root@database-one 09:09:28 [gftest] session1>commit; Query OK, 0 rows affected (0.00 sec) root@database-one 09:18:03 [gftest] session1>select * from testtx; +------+---------+| name | money | +------+---------+| A | 5900.00 | | B | 8100.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
此時(shí),再分別去session2、session3進(jìn)行查詢(xún)。
root@database-one 09:12:28 [gftest] session2> root@database-one 09:18:15 [gftest] session2>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
root@database-one 09:14:22 [gftest] session3> root@database-one 09:18:24 [gftest] session3>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
session2、session3還未看到金額變化。因為他們還在自己的事務(wù)中(由自己session第一個(gè)select * from testtx即隱式開(kāi)啟了事務(wù)),根據REPEATABLE READ事務(wù)隔離的原則確實(shí)不應該看到。
當session2、session3結束當前事務(wù)后,再去查詢(xún)就能看到變化了。
root@database-one 09:18:20 [gftest] session2> root@database-one 09:26:58 [gftest] session2>commit; Query OK, 0 rows affected (0.00 sec) root@database-one 09:27:05 [gftest] session2>select * from testtx; +------+---------+| name | money | +------+---------+| A | 5900.00 | | B | 8100.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
root@database-one 09:18:26 [gftest] session3> root@database-one 09:27:17 [gftest] session3>rollback; Query OK, 0 rows affected (0.00 sec) root@database-one 09:27:24 [gftest] session3>select * from testtx; +------+---------+| name | money | +------+---------+| A | 5900.00 | | B | 8100.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
READ COMMITTED,即使在同一事務(wù)中,每個(gè)consistent read操作都設置并讀取自己的新快照。
我們將數據還原,并調整三個(gè)會(huì )話(huà)的事務(wù)隔離級別均為READ COMMITTED。
root@database-one 09:38:42 [gftest] session1>update testtx set money=6000 where name='A'; Query OK, 0 rows affected (0.00 sec) Rows matched: 1 Changed: 0 Warnings: 0 root@database-one 09:39:20 [gftest] session1>update testtx set money=8000 where name='B'; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 root@database-one 09:39:44 [gftest] session1>commit; Query OK, 0 rows affected (0.00 sec) root@database-one 09:39:49 [gftest] session1>SET SESSION TRANSACTION ISOLATION LEVEL read committed; Query OK, 0 rows affected (0.00 sec) root@database-one 09:40:33 [gftest] session1>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
root@database-one 09:41:31 [gftest] session2>SET SESSION TRANSACTION ISOLATION LEVEL read committed; Query OK, 0 rows affected (0.00 sec) root@database-one 09:41:44 [gftest] session2>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
root@database-one 09:42:16 [gftest] session3>SET SESSION TRANSACTION ISOLATION LEVEL read committed; Query OK, 0 rows affected (0.01 sec) root@database-one 09:42:24 [gftest] session3>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
A給B轉100元。在session1中模擬。
root@database-one 09:40:42 [gftest] session1>update testtx set money=money-100 where name='A'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 root@database-one 09:44:10 [gftest] session1>update testtx set money=money+100 where name='B'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 root@database-one 09:44:20 [gftest] session1>select * from testtx; +------+---------+| name | money | +------+---------+| A | 5900.00 | | B | 8100.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
session1看到了金額進(jìn)行了變化,但還未進(jìn)行提交。
此時(shí),分別去session2、session3進(jìn)行查詢(xún)。
root@database-one 09:42:28 [gftest] session3> root@database-one 09:47:15 [gftest] session3>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
root@database-one 09:42:28 [gftest] session3> root@database-one 09:47:15 [gftest] session3>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
session2、session3均未看到金額變化。
A對轉賬進(jìn)行確認,即提交。
root@database-one 09:50:37 [gftest] session1>commit; Query OK, 0 rows affected (0.03 sec) root@database-one 09:50:43 [gftest] session1>select * from testtx; +------+---------+| name | money | +------+---------+| A | 5900.00 | | B | 8100.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
此時(shí),再分別去session2、session3視角進(jìn)行查詢(xún)。
root@database-one 09:48:02 [gftest] session2> root@database-one 09:52:18 [gftest] session2>select * from testtx; +------+---------+| name | money | +------+---------+| A | 5900.00 | | B | 8100.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
root@database-one 09:48:18 [gftest] session3> root@database-one 09:53:11 [gftest] session3>select * from testtx; +------+---------+| name | money | +------+---------+| A | 5900.00 | | B | 8100.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
session2、session3均看到金額變化。因為他們雖然還在自己的事務(wù)中(由自己session第一個(gè)select * from testtx即隱式開(kāi)啟了事務(wù)),根據READ COMMITTED事務(wù)隔離的原則應該看到。
READ UNCOMMITTED,SELECT語(yǔ)句是以非鎖定方式執行的,但可能會(huì )使用數據的早期版本,這樣的讀取是不一致的,因此也被稱(chēng)為臟讀。
我們將數據還原,并調整三個(gè)會(huì )話(huà)的事務(wù)隔離級別均為READ COMMITTED。
root@database-one 10:02:49 [gftest] session1>update testtx set money=6000 where name='A'; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 root@database-one 10:03:10 [gftest] session1>update testtx set money=8000 where name='B'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 root@database-one 10:03:20 [gftest] session1>commit; Query OK, 0 rows affected (0.00 sec) root@database-one 10:03:30 [gftest] session1>SET SESSION TRANSACTION ISOLATION LEVEL read uncommitted; Query OK, 0 rows affected (0.00 sec) root@database-one 10:03:49 [gftest] session1>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
root@database-one 10:02:52 [gftest] session2>SET SESSION TRANSACTION ISOLATION LEVEL read uncommitted; Query OK, 0 rows affected (0.00 sec) root@database-one 10:04:58 [gftest] session2>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
root@database-one 10:05:35 [gftest] session3>SET SESSION TRANSACTION ISOLATION LEVEL read uncommitted; Query OK, 0 rows affected (0.00 sec) root@database-one 10:05:37 [gftest] session3>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
A給B轉100元。在session1中模擬。
root@database-one 10:06:43 [gftest] session1>update testtx set money=money-100 where name='A'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 root@database-one 10:06:47 [gftest] session1>update testtx set money=money+100 where name='B'; Query OK, 1 row affected (0.00 sec) Rows matched: 1 Changed: 1 Warnings: 0 root@database-one 10:06:57 [gftest] session1>select * from testtx; +------+---------+| name | money | +------+---------+| A | 5900.00 | | B | 8100.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
session1看到了金額進(jìn)行了變化,但還未進(jìn)行提交。
此時(shí),分別去session2、session3進(jìn)行查詢(xún)。
root@database-one 10:05:07 [gftest] session2> root@database-one 10:08:34 [gftest] session2>select * from testtx; +------+---------+| name | money | +------+---------+| A | 5900.00 | | B | 8100.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
root@database-one 10:06:02 [gftest] session3> root@database-one 10:08:42 [gftest] session3>select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
session2看到金額變化,session3未看到金額變化。因為他們雖然還在自己的事務(wù)中(由自己session第一個(gè)select * from testtx即隱式開(kāi)啟了事務(wù)),根據READ UNCOMMITTED事務(wù)隔離的原則,session3沒(méi)有看到金額變化是因為使用了數據的早期版本。這里需要特別注意,有時(shí)可能是session2會(huì )看到金額變化、有時(shí)可能是session3會(huì )看到金額變化、有時(shí)可能是session2和session3都會(huì )看到金額變化、有時(shí)可能是session2和session3都不會(huì )看到金額變化,這個(gè)是由MySQL根據數據的版本情況即時(shí)確定的。
A對轉賬進(jìn)行確認,即提交。
root@database-one 10:35:52 [gftest] session1>commit; Query OK, 0 rows affected (0.01 sec) root@database-one 10:36:01 [gftest] session1>select * from testtx; +------+---------+| name | money | +------+---------+| A | 5900.00 | | B | 8100.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
此時(shí),再分別去session2、session3視角進(jìn)行查詢(xún)。
root@database-one 10:09:24 [gftest] session2> root@database-one 11:09:45 [gftest] session2>select * from testtx; +------+---------+| name | money | +------+---------+| A | 5900.00 | | B | 8100.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
root@database-one 11:08:29 [gftest] session3> root@database-one 11:11:54 [gftest] session3>select * from testtx; +------+---------+| name | money | +------+---------+| A | 5900.00 | | B | 8100.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec)
session2、session3均看到金額變化。
SERIALIZABLE,這個(gè)級別類(lèi)似于REPEATABLE READ,但更嚴格。在非自動(dòng)提交模式下,InnoDB隱式地將所有SELECT語(yǔ)句轉換為SELECT … LOCK IN SHARE MODE。在自動(dòng)提交模式下,SELECT在自己的事務(wù)里,以事務(wù)的原則運行。
因為效果和REPEATABLE READ類(lèi)似,我這里就不再演示了,有興趣的同學(xué)可以自己驗證。SERIALIZABLE執行的規則比REPEATABLE READ更為嚴格,主要用于特殊情況,如XA事務(wù)、解決并發(fā)和死鎖問(wèn)題等場(chǎng)景。
事務(wù)的訪(fǎng)問(wèn)模式很容易理解,就是指在事務(wù)中如何對表中的數據進(jìn)行使用,分為READ WRITE和READ ONLY,默認是READ WRITE。
還是testtx這張表,我們開(kāi)啟一個(gè)READ ONLY事務(wù),對其中的數據進(jìn)行修改,看看會(huì )發(fā)生什么。
root@database-one 11:56: [gftest]> select @@tx_isolation,@@autocommit; +-----------------+--------------+| @@tx_isolation | @@autocommit | +-----------------+--------------+| REPEATABLE-READ | 1 | +-----------------+--------------+1 row in set (0.00 sec) root@database-one 11:57: [gftest]> SET SESSION TRANSACTION read only; Query OK, 0 rows affected (0.00 sec) root@database-one 11:57: [gftest]> start transaction; Query OK, 0 rows affected (0.00 sec) root@database-one 11:59: [gftest]> select * from testtx; +------+---------+| name | money | +------+---------+| A | 6000.00 | | B | 8000.00 | | C | 9000.00 | +------+---------+3 rows in set (0.00 sec) root@database-one 11:59: [gftest]> update testtx set money=0 where name='A'; ERROR 1792 (25006): Cannot execute statement in a READ ONLY transaction.
可以看到,READ ONLY模式的事務(wù)修改數據時(shí)會(huì )報錯。
細心的同學(xué)可能已經(jīng)注意到,在SET TRANSACTION時(shí)有可選關(guān)鍵字GLOBAL和SESSION,它們決定了事務(wù)屬性的作用范圍。
使用GLOBAL時(shí),該語(yǔ)句影響所有后續會(huì )話(huà),現有會(huì )話(huà)不受影響。
使用SESSION時(shí),該語(yǔ)句影響當前會(huì )話(huà)中的所有后續事務(wù)。
不使用GLOBAL或SESSION時(shí),該語(yǔ)句僅影響會(huì )話(huà)中執行的下一個(gè)事務(wù)。
免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自本網(wǎng)站內容采集于網(wǎng)絡(luò )互聯(lián)網(wǎng)轉載等其它媒體和分享為主,內容觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如侵犯了原作者的版權,請告知一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容,聯(lián)系我們QQ:712375056,同時(shí)歡迎投稿傳遞力量。
Copyright ? 2009-2022 56dr.com. All Rights Reserved. 特網(wǎng)科技 特網(wǎng)云 版權所有 特網(wǎng)科技 粵ICP備16109289號
域名注冊服務(wù)機構:阿里云計算有限公司(萬(wàn)網(wǎng)) 域名服務(wù)機構:煙臺帝思普網(wǎng)絡(luò )科技有限公司(DNSPod) CDN服務(wù):阿里云計算有限公司 百度云 中國互聯(lián)網(wǎng)舉報中心 增值電信業(yè)務(wù)經(jīng)營(yíng)許可證B2
建議您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流瀏覽器瀏覽本網(wǎng)站