您好,登錄后才能下訂單哦!
這篇文章主要介紹了mysql事務(wù)的含義是什么的相關(guān)知識,內(nèi)容詳細(xì)易懂,操作簡單快捷,具有一定借鑒價(jià)值,相信大家閱讀完這篇mysql事務(wù)的含義是什么文章都會有所收獲,下面我們一起來看看吧。
mysql事務(wù)是指對數(shù)據(jù)庫執(zhí)行一批操作,在同一個(gè)事務(wù)當(dāng)中,這些操作最終要么全部執(zhí)行成功,要么全部失敗,不會存在部分成功的情況;事務(wù)是一個(gè)原子操作,是一個(gè)最小執(zhí)行單元,可以由一個(gè)或多個(gè)SQL語句組成。
數(shù)據(jù)庫中的事務(wù)是指對數(shù)據(jù)庫執(zhí)行一批操作,在同一個(gè)事務(wù)當(dāng)中,這些操作最終要么全部執(zhí)行成功,要么全部失敗,不會存在部分成功的情況。
事務(wù)是一個(gè)原子操作。是一個(gè)最小執(zhí)行單元??梢杂梢粋€(gè)或多個(gè)SQL語句組成
在同一個(gè)事務(wù)當(dāng)中,所有的SQL語句都成功執(zhí)行時(shí),整 個(gè)事務(wù)成功,有一個(gè)SQL語句執(zhí)行失敗,整個(gè)事務(wù)都執(zhí)行失敗。
舉個(gè)例子:
比如A用戶給B用戶轉(zhuǎn)賬100操作,過程如下:
從A賬戶扣100
給B賬戶加100
如果在事務(wù)的支持下,上面最終只有2種結(jié)果:
操作成功:A賬戶減少100;B賬戶增加100
操作失?。篈、B兩個(gè)賬戶都沒有發(fā)生變化
如果沒有事務(wù)的支持,可能出現(xiàn)錯(cuò):A賬戶減少了100,此時(shí)系統(tǒng)掛了,導(dǎo)致B賬戶沒有加上100,而A賬戶憑空少了100。
事務(wù)的整個(gè)過程如原子操作一樣,最終要么全部成功,或者全部失敗,這個(gè)原子性是從最終結(jié)果來看的,從最終結(jié)果來看這個(gè)過程是不可分割的。
一個(gè)事務(wù)必須使數(shù)據(jù)庫從一個(gè)一致性狀態(tài)變換到另一個(gè)一致性狀態(tài)。
首先回顧一下一致性的定義。所謂一致性,指的是數(shù)據(jù)處于一種有意義的狀態(tài),這種狀態(tài)是語義上的而不是語法上的。最常見的例子是轉(zhuǎn)帳。例如從帳戶A轉(zhuǎn)一筆錢到帳戶B上,如果帳戶A上的錢減少了,而帳戶B上的錢卻沒有增加,那么我們認(rèn)為此時(shí)數(shù)據(jù)處于不一致的狀態(tài)。
從這段話的理解來看,所謂一致性,即,從實(shí)際的業(yè)務(wù)邏輯上來說,最終結(jié)果是對的、是跟程序員的所期望的結(jié)果完全符合的
一個(gè)事務(wù)的執(zhí)行不能被其他事務(wù)干擾。即一個(gè)事務(wù)內(nèi)部的操作及使用的數(shù)據(jù)對并發(fā)的其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個(gè)事務(wù)之間不能互相干擾。
這里先提一下事務(wù)的隔離級別:
讀未提交:read uncommitted
讀已提交:read committed
可重復(fù)讀:repeatable read
串行化:serializable
一個(gè)事務(wù)一旦提交,他對數(shù)據(jù)庫中數(shù)據(jù)的改變就應(yīng)該是永久性的。當(dāng)事務(wù)提交之后,數(shù)據(jù)會持久化到硬盤,修改是永久性的。
mysql中事務(wù)默認(rèn)是隱式事務(wù),執(zhí)行insert、update、delete操作的時(shí)候,數(shù)據(jù)庫自動開啟事務(wù)、提交或回滾事務(wù)。
是否開啟隱式事務(wù)是由變量autocommit控制的。
所以事務(wù)分為隱式事務(wù)和顯式事務(wù)。
事務(wù)自動開啟、提交或回滾,比如insert、update、delete語句,事務(wù)的開啟、提交或回滾由mysql內(nèi)部自動控制的。
查看變量autocommit是否開啟了自動提交
mysql> show variables like 'autocommit';+---------------+-------+| Variable_name | Value |+---------------+-------+| autocommit | ON |+---------------+-------+1 row in set, 1 warning (0.00 sec)
autocommit為ON表示開啟了自動提交。
事務(wù)需要手動開啟、提交或回滾,由開發(fā)者自己控制。
2種方式手動控制事務(wù):
語法:
//設(shè)置不自動提交事務(wù)set autocommit=0;//執(zhí)行事務(wù)操作commit|rollback;
示例1:提交事務(wù)操作,如下:
mysql> create table test1 (a int);Query OK, 0 rows affected (0.01 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> set autocommit=0;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values(1);Query OK, 1 row affected (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a |+------+| 1 |+------+1 row in set (0.00 sec)
示例2:回滾事務(wù)操作,如下:
mysql> set autocommit=0;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values(2);Query OK, 1 row affected (0.00 sec)mysql> rollback;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a |+------+| 1 |+------+1 row in set (0.00 sec)
可以看到上面數(shù)據(jù)回滾了。
我們把a(bǔ)utocommit還原回去:
mysql> set autocommit=1;Query OK, 0 rows affected (0.00 sec)
語法:
start transaction;//開啟事務(wù)//執(zhí)行事務(wù)操作commit|rollback;
示例1:提交事務(wù)操作,如下:
mysql> select * from test1;+------+| a |+------+| 1 |+------+1 row in set (0.00 sec)mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (2);Query OK, 1 row affected (0.00 sec)mysql> insert into test1 values (3);Query OK, 1 row affected (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a |+------+| 1 || 2 || 3 |+------+3 rows in set (0.00 sec)
上面成功插入了2條數(shù)據(jù)。
示例2:回滾事務(wù)操作,如下:
mysql> select * from test1;+------+| a |+------+| 1 || 2 || 3 |+------+3 rows in set (0.00 sec)mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> delete from test1;Query OK, 3 rows affected (0.00 sec)mysql> rollback;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a |+------+| 1 || 2 || 3 |+------+3 rows in set (0.00 sec)
上面事務(wù)中我們刪除了test1的數(shù)據(jù),顯示刪除了3行,最后回滾了事務(wù)。
在事務(wù)中我們執(zhí)行了一大批操作,可能我們只想回滾部分?jǐn)?shù)據(jù),怎么做呢?
我們可以將一大批操作分為幾個(gè)部分,然后指定回滾某個(gè)部分??梢允褂胹avepoin來實(shí)現(xiàn),效果如下:
先清除test1表數(shù)據(jù):
mysql> delete from test1;Query OK, 3 rows affected (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)
演示savepoint效果,認(rèn)真看:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (1);Query OK, 1 row affected (0.00 sec)mysql> savepoint part1;//設(shè)置一個(gè)保存點(diǎn)Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (2);Query OK, 1 row affected (0.00 sec)mysql> rollback to part1;//將savepint = part1的語句到當(dāng)前語句之間所有的操作回滾Query OK, 0 rows affected (0.00 sec)mysql> commit;//提交事務(wù)Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a |+------+| 1 |+------+1 row in set (0.00 sec)
從上面可以看出,執(zhí)行了2次插入操作,最后只插入了1條數(shù)據(jù)。
savepoint需要結(jié)合rollback to sp1一起使用,可以將保存點(diǎn)sp1到rollback to之間的操作回滾掉。
表示在事務(wù)中執(zhí)行的是一些只讀操作,如查詢,但是不會做insert、update、delete操作,數(shù)據(jù)庫內(nèi)部對只讀事務(wù)可能會有一些性能上的優(yōu)化。
用法如下:
start transaction read only;
示例:
mysql> commit;Query OK, 0 rows affected (0.00 sec)mysql> start transaction read only;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a |+------+| 1 || 1 |+------+2 rows in set (0.00 sec)mysql> delete from test1;ERROR 1792 (25006): Cannot execute statement in a READ ONLY transaction.mysql> commit;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a |+------+| 1 || 1 |+------+2 rows in set (0.00 sec)
只讀事務(wù)中執(zhí)行delete會報(bào)錯(cuò)。
這些問題主要是基于數(shù)據(jù)在多個(gè)事務(wù)中的可見性來說的。也是并發(fā)事務(wù)產(chǎn)生的問題。
丟失更新就是兩個(gè)不同的事務(wù)(或者Java程序線程)在某一時(shí)刻對同一數(shù)據(jù)進(jìn)行讀取后,先后進(jìn)行修改。導(dǎo)致第一次操作數(shù)據(jù)丟失。
「
第一類丟失更新 :A,B 事務(wù)同時(shí)操作同一數(shù)據(jù),A先對改數(shù)據(jù)進(jìn)行了更改,B再次更改時(shí)失敗然后回滾,把A更新的數(shù)據(jù)也回滾了。(事務(wù)撤銷造成的撤銷丟失)
第二類丟失更新:A,B 事務(wù)同時(shí)操作同一數(shù)據(jù),A先對改數(shù)據(jù)進(jìn)行了更改,B再次更改并且提交,把A提交的數(shù)據(jù)給覆蓋了。(事務(wù)提交造成的覆蓋丟失)
」
一個(gè)事務(wù)在執(zhí)行的過程中讀取到了其他事務(wù)還沒有提交的數(shù)據(jù)。 這個(gè)還是比較好理解的。
「
兩個(gè)事務(wù)同時(shí)操作同一數(shù)據(jù),A事務(wù)對該數(shù)據(jù)進(jìn)行了修改還沒提交的時(shí)候,B事務(wù)訪問了該條事務(wù),并且使用了該數(shù)據(jù),此時(shí)A事務(wù)回滾,那么B事務(wù)讀到的就是臟數(shù)據(jù)。
比如事務(wù)1,修改了某個(gè)數(shù)據(jù) 事務(wù)2,剛好訪問了事務(wù)1修改后的數(shù)據(jù)
此時(shí)事務(wù)1,回滾了操作 事務(wù)2,讀到還是回滾前的數(shù)據(jù)
」
從字面上我們就可以理解,即一個(gè)事務(wù)操作過程中可以讀取到其他事務(wù)已經(jīng)提交的數(shù)據(jù)。
事務(wù)中的每次讀取操作,讀取到的都是數(shù)據(jù)庫中其他事務(wù)已提交的最新的數(shù)據(jù)(相當(dāng)于當(dāng)前讀)
在同一事務(wù)中,多次讀取同一數(shù)據(jù)返回的結(jié)果有所不同,換句話說,后續(xù)讀取可以讀到另一事務(wù)已提交的更新數(shù)據(jù)。相反,“可重復(fù)讀” 在同一事務(wù)中多次讀取數(shù)據(jù)時(shí), 能夠保證所讀數(shù)據(jù)一樣, 也就是后續(xù)讀取不能讀到另一事務(wù)已提交的更新數(shù)據(jù)。
「
這種情況發(fā)生 在一個(gè)事務(wù)內(nèi)多次讀同一數(shù)據(jù)。A事務(wù)查詢某條數(shù)據(jù),該事務(wù)未結(jié)束時(shí),B事務(wù)也訪問同一數(shù)據(jù)并進(jìn)行了修改。那么在A事務(wù)中的兩 次讀數(shù)據(jù)之間,由于第二個(gè)事務(wù)的修改,那么第一個(gè)事務(wù)兩次讀到的的數(shù)據(jù)可能是不一樣的。
事務(wù)1,查詢某個(gè)數(shù)據(jù) 事務(wù)2,修改了某個(gè)數(shù)據(jù),提交
事務(wù)1,再次查詢這個(gè)數(shù)據(jù)
這樣事務(wù)1兩次查詢的數(shù)據(jù)不一樣,稱為不可重復(fù)讀
」
一個(gè)事務(wù)操作中對于一個(gè)讀取操作不管多少次,讀取到的結(jié)果都是一樣的。
臟讀、不可重復(fù)讀、可重復(fù)讀、幻讀,其中最難理解的是幻讀
以mysql為例:
幻讀現(xiàn)象例子:
可重復(fù)讀模式下,比如有個(gè)用戶表,手機(jī)號碼為主鍵,有兩個(gè)事物進(jìn)行如下操作
事務(wù)A操作如下: 1、打開事務(wù) 2、查詢號碼為X的記錄,不存在 3、插入號碼為X的數(shù)據(jù),插入報(bào)錯(cuò)(為什么會報(bào)錯(cuò),先向下看) 4、查詢號碼為X的記錄,發(fā)現(xiàn)還是不存在(由于是可重復(fù)讀,所以讀取記錄X還是不存在的)
事物B操作:在事務(wù)A第2步操作時(shí)插入了一條X的記錄,所以會導(dǎo)致A中第3步插入報(bào)錯(cuò)(違反了唯一約束)
上面操作對A來說就像發(fā)生了幻覺一樣,明明查詢X(A中第二步、第四步)不存在,但卻無法插入成功
幻讀可以這么理解:事務(wù)中后面的操作(插入號碼X)需要上面的讀取操作(查詢號碼X的記錄)提供支持,但讀取操作卻不能支持下面的操作時(shí)產(chǎn)生的錯(cuò)誤,就像發(fā)生了幻覺一樣。
看第二種解釋:
事務(wù)A在操作一堆數(shù)據(jù)的時(shí)候,事務(wù)B插入了一條數(shù)據(jù),A事務(wù)再次(第二次)查詢,發(fā)現(xiàn)多了一條數(shù)據(jù),像是幻覺。與不可重復(fù)讀類似,不同的是一個(gè)是修改刪除操作,一個(gè)是新增操作。
如果還是理解不了的,繼續(xù)向下看,后面后詳細(xì)的演示。
當(dāng)多個(gè)事務(wù)同時(shí)進(jìn)行的時(shí)候,如何確保當(dāng)前事務(wù)中數(shù)據(jù)的正確性,比如A、B兩個(gè)事物同時(shí)進(jìn)行的時(shí)候,A是否可以看到B已提交的數(shù)據(jù)或者B未提交的數(shù)據(jù),這個(gè)需要依靠事務(wù)的隔離級別來保證,不同的隔離級別中所產(chǎn)生的效果是不一樣的。
事務(wù)隔離級別主要是解決了上面多個(gè)事務(wù)之間數(shù)據(jù)可見性及數(shù)據(jù)正確性的問題。(或者說為了解決并發(fā)控制可能產(chǎn)生的異常問題,數(shù)據(jù)庫定義了四種事務(wù)的隔離級別)
隔離級別分為4種:
讀未提交:READ-UNCOMMITTED
讀已提交:READ-COMMITTED
可重復(fù)讀:REPEATABLE-READ
串行:SERIALIZABLE
上面4中隔離級別越來越強(qiáng),會導(dǎo)致數(shù)據(jù)庫的并發(fā)性也越來越低。
mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name | Value |+-----------------------+----------------+| transaction_isolation | READ-COMMITTED |+-----------------------+----------------+1 row in set, 1 warning (0.00 sec)
分2步驟,修改文件、重啟mysql,如下:
修改mysql中的my.init文件,我們將隔離級別設(shè)置為:READ-UNCOMMITTED,如下:
# 隔離級別設(shè)置,READ-UNCOMMITTED讀未提交,READ-COMMITTED讀已提交,REPEATABLE-READ可重復(fù)讀,SERIALIZABLE串行transaction-isolation=READ-UNCOMMITTED
以管理員身份打開cmd窗口,重啟mysql,如下:
C:\Windows\system32>net stop mysql
mysql 服務(wù)正在停止..mysql 服務(wù)已成功停止。
C:\Windows\system32>net start mysql
mysql 服務(wù)正在啟動 .mysql 服務(wù)已經(jīng)啟動成功。
隔離級別 | 臟讀可能性 | 不可重復(fù)讀可能性 | 幻讀可能性 |
---|---|---|---|
READ-UNCOMMITTED | 有 | 有 | 有 |
READ-COMMITTED | 無 | 有 | 有 |
REPEATABLE-READ | 無 | 無 | 有 |
SERIALIZABLE | 無 | 無 | 無 |
下面我們來演示一下,各種隔離級別中可見性的問題,開啟兩個(gè)窗口,叫做A、B窗口,兩個(gè)窗口中登錄mysql。
將隔離級別置為READ-UNCOMMITTED:
# 隔離級別設(shè)置,READ-UNCOMMITTED讀未提交,READ-COMMITTED讀已提交,REPEATABLE-READ可重復(fù)讀,SERIALIZABLE串行transaction-isolation=READ-UNCOMMITTED
重啟mysql:
C:\Windows\system32>net stop mysql
mysql 服務(wù)正在停止..mysql 服務(wù)已成功停止。
C:\Windows\system32>net start mysql
mysql 服務(wù)正在啟動 .mysql 服務(wù)已經(jīng)啟動成功。
查看隔離級別:
mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name | Value |+-----------------------+----------------+| transaction_isolation | READ-UNCOMMITTED |+-----------------------+----------------+1 row in set, 1 warning (0.00 sec)
先清空test1表數(shù)據(jù):
delete from test1;select * from test1;
按時(shí)間順序在2個(gè)窗口中執(zhí)行下面操作:
時(shí)間 | 窗口A | 窗口B |
---|---|---|
T1 | start transaction; | |
T2 | select * from test1; | |
T3 | start transaction; | |
T4 | insert into test1 values (1); | |
T5 | select * from test1; | |
T6 | select * from test1; | |
T7 | commit; | |
T8 | commit; |
A窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;+------+| a |+------+| 1 |+------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
B窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (1);Query OK, 1 row affected (0.00 sec)mysql> select * from test1;+------+| a |+------+| 1 |+------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
看一下:
T2-A:無數(shù)據(jù),T6-A:有數(shù)據(jù),T6時(shí)刻B還未提交,此時(shí)A已經(jīng)看到了B插入的數(shù)據(jù),說明出現(xiàn)了臟讀。
T2-A:無數(shù)據(jù),T6-A:有數(shù)據(jù),查詢到的結(jié)果不一樣,說明不可重復(fù)讀。
結(jié)論:讀未提交情況下,可以讀取到其他事務(wù)還未提交的數(shù)據(jù),多次讀取結(jié)果不一樣,出現(xiàn)了臟讀、不可重復(fù)讀、幻讀
將隔離級別置為READ-COMMITTED
# 隔離級別設(shè)置,READ-UNCOMMITTED讀未提交,READ-COMMITTED讀已提交,REPEATABLE-READ可重復(fù)讀,SERIALIZABLE串行transaction-isolation=READ-COMMITTED
重啟mysql:
C:\Windows\system32>net stop mysql
mysql 服務(wù)正在停止..mysql 服務(wù)已成功停止。
C:\Windows\system32>net start mysql
mysql 服務(wù)正在啟動 .mysql 服務(wù)已經(jīng)啟動成功。
查看隔離級別:
mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name | Value |+-----------------------+----------------+| transaction_isolation | READ-COMMITTED |+-----------------------+----------------+1 row in set, 1 warning (0.00 sec)
先清空test1表數(shù)據(jù):
delete from test1;select * from test1;
按時(shí)間順序在2個(gè)窗口中執(zhí)行下面操作:
時(shí)間 | 窗口A | 窗口B |
---|---|---|
T1 | start transaction; | |
T2 | select * from test1; | |
T3 | start transaction; | |
T4 | insert into test1 values (1); | |
T5 | select * from test1; | |
T6 | select * from test1; | |
T7 | commit; | |
T8 | select * from test1; | |
T9 | commit; |
A窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;+------+| a |+------+| 1 |+------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
B窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (1);Query OK, 1 row affected (0.00 sec)mysql> select * from test1;+------+| a |+------+| 1 |+------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
看一下:
T5-B:有數(shù)據(jù),T6-A窗口:無數(shù)據(jù),A看不到B的數(shù)據(jù),說明沒有臟讀。
T6-A窗口:無數(shù)據(jù),T8-A:看到了B插入的數(shù)據(jù),此時(shí)B已經(jīng)提交了,A看到了B已提交的數(shù)據(jù),說明可以讀取到已提交的數(shù)據(jù)。
T2-A、T6-A:無數(shù)據(jù),T8-A:有數(shù)據(jù),多次讀取結(jié)果不一樣,說明不可重復(fù)讀。
結(jié)論:讀已提交情況下,無法讀取到其他事務(wù)還未提交的數(shù)據(jù),可以讀取到其他事務(wù)已經(jīng)提交的數(shù)據(jù),多次讀取結(jié)果不一樣,未出現(xiàn)臟讀,出現(xiàn)了讀已提交、不可重復(fù)讀、幻讀
將隔離級別置為REPEATABLE-READ
# 隔離級別設(shè)置,READ-UNCOMMITTED讀未提交,READ-COMMITTED讀已提交,REPEATABLE-READ可重復(fù)讀,SERIALIZABLE串行transaction-isolation=REPEATABLE-READ
重啟mysql:
C:\Windows\system32>net stop mysql
mysql 服務(wù)正在停止..mysql 服務(wù)已成功停止。
C:\Windows\system32>net start mysql
mysql 服務(wù)正在啟動 .mysql 服務(wù)已經(jīng)啟動成功。
查看隔離級別:
mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name | Value |+-----------------------+----------------+| transaction_isolation | REPEATABLE-READ |+-----------------------+----------------+1 row in set, 1 warning (0.00 sec)
先清空test1表數(shù)據(jù):
delete from test1;select * from test1;
按時(shí)間順序在2個(gè)窗口中執(zhí)行下面操作:
時(shí)間 | 窗口A | 窗口B |
---|---|---|
T1 | start transaction; | |
T2 | select * from test1; | |
T3 | start transaction; | |
T4 | insert into test1 values (1); | |
T5 | select * from test1; | |
T6 | select * from test1; | |
T7 | commit; | |
T8 | select * from test1; | |
T9 | commit; | |
T10 | select * from test1; |
A窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> select * from test1;Empty set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)mysql> select * from test1;+------+| a |+------+| 1 || 1 |+------+2 rows in set (0.00 sec)
B窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into test1 values (1);Query OK, 1 row affected (0.00 sec)mysql> select * from test1;+------+| a |+------+| 1 || 1 |+------+2 rows in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
看一下:
T2-A、T6-A窗口:無數(shù)據(jù),T5-B:有數(shù)據(jù),A看不到B的數(shù)據(jù),說明沒有臟讀。
T8-A:無數(shù)據(jù),此時(shí)B已經(jīng)提交了,A看不到B已提交的數(shù)據(jù),A中3次讀的結(jié)果一樣都是沒有數(shù)據(jù)的,說明可重復(fù)讀。
結(jié)論:可重復(fù)讀情況下,未出現(xiàn)臟讀,未讀取到其他事務(wù)已提交的數(shù)據(jù),多次讀取結(jié)果一致,即可重復(fù)讀。
將隔離級別置為REPEATABLE-READ
# 隔離級別設(shè)置,READ-UNCOMMITTED讀未提交,READ-COMMITTED讀已提交,REPEATABLE-READ可重復(fù)讀,SERIALIZABLE串行transaction-isolation=REPEATABLE-READ
重啟mysql:
C:\Windows\system32>net stop mysql
mysql 服務(wù)正在停止..mysql 服務(wù)已成功停止。
C:\Windows\system32>net start mysql
mysql 服務(wù)正在啟動 .mysql 服務(wù)已經(jīng)啟動成功。
查看隔離級別:
mysql> show variables like 'transaction_isolation';+-----------------------+----------------+| Variable_name | Value |+-----------------------+----------------+| transaction_isolation | REPEATABLE-READ |+-----------------------+----------------+1 row in set, 1 warning (0.00 sec)
準(zhǔn)備數(shù)據(jù):
mysql> create table t_user(id int primary key,name varchar(16) unique key);Query OK, 0 rows affected (0.01 sec)mysql> insert into t_user values (1,'路人甲Java'),(2,'路人甲Java');ERROR 1062 (23000): Duplicate entry '路人甲Java' ***\*for\**** key 'name'mysql> select * from t_user;Empty set (0.00 sec)
上面我們創(chuàng)建t_user表,name添加了唯一約束,表示name不能重復(fù),否則報(bào)錯(cuò)。
按時(shí)間順序在2個(gè)窗口中執(zhí)行下面操作:
時(shí)間 | 窗口A | 窗口B |
---|---|---|
T1 | start transaction; | |
T2 | start transaction; | |
T3 | – 插入路人甲Java insert into t_user values (1,‘路人甲Java’); | |
T4 | select * from t_user; | |
T5 | – 查看路人甲Java是否存在 select * from t_user where name=‘路人甲Java’; | |
T6 | commit; | |
T7 | – 插入路人甲Java insert into t_user values (2,‘路人甲Java’); | |
T8 | – 查看路人甲Java是否存在 select * from t_user where name=‘路人甲Java’; | |
T9 | commit; |
A窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> select * from t_user where name='路人甲Java';Empty set (0.00 sec)mysql> insert into t_user values (2,'路人甲Java');ERROR 1062 (23000): Duplicate entry '路人甲Java' ***\*for\**** key 'name'mysql> select * from t_user where name='路人甲Java';Empty set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
B窗口如下:
mysql> start transaction;Query OK, 0 rows affected (0.00 sec)mysql> insert into t_user values (1,'路人甲Java');Query OK, 1 row affected (0.00 sec)mysql> select * from t_user;+----+---------------+| id | name |+----+---------------+| 1 | 路人甲Java |+----+---------------+1 row in set (0.00 sec)mysql> commit;Query OK, 0 rows affected (0.00 sec)
看一下:
A想插入數(shù)據(jù)路人甲Java,插入之前先查詢了一下(T5時(shí)刻)該用戶是否存在,發(fā)現(xiàn)不存在,然后在T7時(shí)刻執(zhí)行插入,報(bào)錯(cuò)了,報(bào)數(shù)據(jù)已經(jīng)存在了,因?yàn)門6時(shí)刻B已經(jīng)插入了路人甲Java。
然后A有點(diǎn)郁悶,剛才查的時(shí)候不存在的,然后A不相信自己的眼睛,又去查一次(T8時(shí)刻),發(fā)現(xiàn)路人甲Java還是不存在的。
此時(shí)A心里想:數(shù)據(jù)明明不存在啊,為什么無法插入呢?這不是懵逼了么,A覺得如同發(fā)生了幻覺一樣。
SERIALIZABLE會讓并發(fā)的事務(wù)串行執(zhí)行(多個(gè)事務(wù)之間讀寫、寫讀、寫寫會產(chǎn)生互斥,效果就是串行執(zhí)行,多個(gè)事務(wù)之間的讀讀不會產(chǎn)生互斥)。
讀寫互斥:事務(wù)A中先讀取操作,事務(wù)B發(fā)起寫入操作,事務(wù)A中的讀取會導(dǎo)致事務(wù)B中的寫入處于等待狀態(tài),直到A事務(wù)完成為止。
表示我開啟一個(gè)事務(wù),為了保證事務(wù)中不會出現(xiàn)上面說的問題(臟讀、不可重復(fù)讀、讀已提交、幻讀),那么我讀取的時(shí)候,其他事務(wù)有修改數(shù)據(jù)的操作需要排隊(duì)等待,等待我讀取完成之后,他們才可以繼續(xù)。
寫讀、寫寫也是互斥的,讀寫互斥類似。
這個(gè)類似于java中的java.util.concurrent.lock.ReentrantReadWriteLock類產(chǎn)生的效果。
下面演示讀寫互斥的效果。
將隔離級別置為SERIALIZABLE
# 隔離級別設(shè)置,READ-UNCOMMITTED讀未提交,READ-COMMITTED讀已提交,REPEATABLE-READ可重復(fù)讀,SERIALIZABLE串行transaction-isolation=SERIALIZABLE
重啟mysql:
C:\Windows\system32>net stop mysql
mysql 服務(wù)正在停止..mysql 服務(wù)已成功停止。
C:\Windows\system32>net start mysql
mysql 服務(wù)正在啟動 .mysql 服務(wù)已經(jīng)啟動成功。
查看隔離級別:
mysql> show variables like 'transaction_isolation';+-----------------------+--------------+| Variable_name | Value |+-----------------------+--------------+| transaction_isolation | SERIALIZABLE |+-----------------------+--------------+1 row in set, 1 warning (0.00 sec)
先清空test1表數(shù)據(jù):
delete from test1;select * from test1;
按時(shí)間順序在2個(gè)窗口中執(zhí)行下面操作:
時(shí)間 | 窗口A | 窗口B |
---|---|---|
T1 | start transaction; | |
T2 | select * from test1; | |
T3 | start transaction; | |
T4 | insert into test1 values (1); | |
T5 | commit; | |
T6 | commit; |
按時(shí)間順序運(yùn)行上面的命令,會發(fā)現(xiàn)T4-B這樣會被阻塞,直到T5-A執(zhí)行完畢。
上面這個(gè)演示的是讀寫互斥產(chǎn)生的效果,大家可以自己去寫一下寫讀、寫寫互斥的效果。
可以看出來,事務(wù)只能串行執(zhí)行了。串行情況下不存在臟讀、不可重復(fù)讀、幻讀的問題了。
讀未提交( Read Uncommitted )
讀未提交是隔離級別最低的一種事務(wù)級別。在這種隔離級別下,一個(gè)事務(wù)會讀到另一個(gè)事務(wù)更新后但未提交的數(shù)據(jù),如果另一個(gè)事務(wù)回滾,那么當(dāng)前事務(wù)讀到的數(shù)據(jù)就是臟數(shù)據(jù),這就是臟讀(Dirty Read)。
讀已提交( Read Committed )
在 Read Committed 隔離級別下,一個(gè)事務(wù)可能會遇到不可重復(fù)讀(Non Repeatable Read)的問題。不可重復(fù)讀是指,在一個(gè)事務(wù)內(nèi),多次讀同一數(shù)據(jù),在這個(gè)事務(wù)還沒有結(jié)束時(shí),如果另一個(gè)事務(wù)恰好修改了這個(gè)數(shù)據(jù),那么,在第一個(gè)事務(wù)中,兩次讀取的數(shù)據(jù)就可能不一致。
可重復(fù)讀( Repeatable Read )
在Repeatable Read隔離級別下,一個(gè)事務(wù)可能會遇到幻讀(Phantom Read)的問題?;米x是指,在一個(gè)事務(wù)中,第一次查詢某條記錄,發(fā)現(xiàn)沒有,但是,當(dāng)試圖更新這條不存在的記錄時(shí),竟然能成功,并且,再次讀取同一條記錄,它就神奇地出現(xiàn)了。幻讀就是沒有讀到的記錄,以為不存在,但其實(shí)是可以更新成功的,并且,更新成功后,再次讀取,就出現(xiàn)了。
可串行化( Serializable )
Serializable 是最嚴(yán)格的隔離級別。在Serializable隔離級別下,所有事務(wù)按照次序依次執(zhí)行,因此,臟讀、不可重復(fù)讀、幻讀都不會出現(xiàn)。
雖然 Serializable 隔離級別下的事務(wù)具有最高的安全性,但是,由于事務(wù)是串行執(zhí)行,所以效率會大大下降,應(yīng)用程序的性能會急劇降低。如果沒有特別重要的情景,一般都不會使用Serializable隔離級別。
默認(rèn)隔離級別:如果沒有指定隔離級別,數(shù)據(jù)庫就會使用默認(rèn)的隔離級別。在MySQL中,如果使用 InnoDB,默認(rèn)的隔離級別是Repeatable Read。
需要對各種隔離級別產(chǎn)生的現(xiàn)象非常了解,然后選擇的時(shí)候才能游刃有余
隔離級別越高,并發(fā)性也低,比如最高級別SERIALIZABLE會讓事物串行執(zhí)行,并發(fā)操作變成串行了,會導(dǎo)致系統(tǒng)性能直接降低。
具體選擇哪種需要結(jié)合具體的業(yè)務(wù)來選擇。
讀已提交(READ-COMMITTED)通常用的比較多。
關(guān)于“mysql事務(wù)的含義是什么”這篇文章的內(nèi)容就介紹到這里,感謝各位的閱讀!相信大家對“mysql事務(wù)的含義是什么”知識都有一定的了解,大家如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。