什么是事務(wù)(Transaction)
事務(wù)是指一組操作集合稚照,這一組操作執(zhí)行完成之后必須滿足 ACID 特性被啼。
- 原子性(Atomic): 一組操作要么全部執(zhí)行成功盟迟,要么全部執(zhí)行失斞辜;
- 一致性(Consistency): 事務(wù)執(zhí)行成功后骤坐,數(shù)據(jù)必須保持一致绪杏。例如轉(zhuǎn)賬事務(wù)下愈,不管有多少個(gè)轉(zhuǎn)賬并發(fā)纽绍,戶頭的錢款總數(shù)還是固定一致的;
- 隔離性(Isolation): 并發(fā)事務(wù)所作的修改必須與任何其它并發(fā)事務(wù)所作的修改隔離势似。串行處理事務(wù)能保證絕對(duì)的隔離拌夏,但考慮到性能的問(wèn)題,可以適當(dāng)降低隔離程度以提高并發(fā)效率履因;
- 持久性(Duration): 事務(wù)執(zhí)行后障簿,對(duì)數(shù)據(jù)的影響是永久性的,即使系統(tǒng)出現(xiàn)故障也會(huì)繼續(xù)保持栅迄。
對(duì)于數(shù)據(jù)庫(kù)事務(wù)((Database Transaction)站故,這一組操作集合就是指多個(gè)SQL語(yǔ)句。這組SQL語(yǔ)句執(zhí)行時(shí)需要保證ACID的特性毅舆。
什么是事務(wù)隔離西篓?
一個(gè)事務(wù)要保證ACID的特性,事務(wù)隔離就是指其中的隔離性(Isolation)憋活。
隔離性越高岂津,系統(tǒng)吞吐量越低。
所以我們?cè)诳紤]事務(wù)隔離問(wèn)題時(shí)悦即,本質(zhì)上是在考慮一個(gè)trade off的問(wèn)題:
- 隔離性
- 吞吐量
事務(wù)隔離的級(jí)別
事務(wù)隔離的級(jí)別有四個(gè)級(jí)別:
- Read Uncommitted
- Read Committed
- Repeatable Read
- Serializable
這四個(gè)級(jí)別的主要區(qū)分在于:
一個(gè)事務(wù)執(zhí)行時(shí)能否讀取到其他事務(wù)對(duì)數(shù)據(jù)所做的修改吮成。
PS: 最好的理解事務(wù)隔離級(jí)別的方式就是從隔離方式的命名去理解。
第一級(jí)別:Read Uncommitted
Read Uncommitted辜梳,當(dāng)前事務(wù)讀取到其他未提交(uncommitted)的事務(wù)所做的數(shù)據(jù)更新粱甫。
這種現(xiàn)象通常也叫作 臟讀 (Dirty Read)
。
#首先作瞄,修改隔離級(jí)別
set tx_isolation='READ-UNCOMMITTED';
select @@tx_isolation;
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+
#事務(wù)A:?jiǎn)?dòng)一個(gè)事務(wù)
start transaction;
select * from tx;
+------+------+
| id | num |
+------+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+------+------+
#事務(wù)B:也啟動(dòng)一個(gè)事務(wù)(那么兩個(gè)事務(wù)交叉了)
#在事務(wù)B中執(zhí)行更新語(yǔ)句魔种,且不提交
start transaction;
update tx set num=10 where id=1;
select * from tx;
+------+------+
| id | num |
+------+------+
| 1 | 10 |
| 2 | 2 |
| 3 | 3 |
+------+------+
#事務(wù)A:那么這時(shí)候事務(wù)A能看到這個(gè)更新了的數(shù)據(jù)嗎?
select * from tx;
+------+------+
| id | num |
+------+------+
| 1 | 10 | --->可以看到!說(shuō)明我們讀到了事務(wù)B還沒(méi)有提交的數(shù)據(jù)
| 2 | 2 |
| 3 | 3 |
+------+------+
#事務(wù)B:事務(wù)B回滾,仍然未提交
rollback;
select * from tx;
+------+------+
| id | num |
+------+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+------+------+
#事務(wù)A:在事務(wù)A里面看到的也是B沒(méi)有提交的數(shù)據(jù)
select * from tx;
+------+------+
| id | num |
+------+------+
| 1 | 1 | --->臟讀意味著我在這個(gè)事務(wù)中(A中)粉洼,事務(wù)B雖然沒(méi)有提交节预,但它任何一條數(shù)據(jù)變化叶摄,我都可以看到!
| 2 | 2 |
| 3 | 3 |
+------+------+
第二級(jí)別:Read Committed
Read committed安拟,當(dāng)前事務(wù)讀取到其他已提交(committed)的事務(wù)所做的數(shù)據(jù)更新蛤吓。
該隔離級(jí)別會(huì)帶來(lái)另一個(gè)現(xiàn)象,不可重復(fù)讀(Non-repeatable Read)
糠赦,是指一個(gè)事務(wù)執(zhí)行期間会傲,因?yàn)槠渌聞?wù)所做的數(shù)據(jù)更新,可能會(huì)存在多次讀取數(shù)據(jù)不一致的現(xiàn)象拙泽。
目前淌山,該級(jí)別通常是大部分?jǐn)?shù)據(jù)庫(kù)的默認(rèn)隔離級(jí)別。 Mysql是例外顾瞻,它的隔離級(jí)別是第三級(jí)別泼疑,后續(xù)會(huì)有介紹。
#首先修改隔離級(jí)別
set tx_isolation='read-committed';
select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| READ-COMMITTED |
+----------------+
#事務(wù)A:?jiǎn)?dòng)一個(gè)事務(wù)
start transaction;
select * from tx;
+------+------+
| id | num |
+------+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+------+------+
#事務(wù)B:也啟動(dòng)一個(gè)事務(wù)(那么兩個(gè)事務(wù)交叉了)
在這事務(wù)中更新數(shù)據(jù)荷荤,且未提交
start transaction;
update tx set num=10 where id=1;
select * from tx;
+------+------+
| id | num |
+------+------+
| 1 | 10 |
| 2 | 2 |
| 3 | 3 |
+------+------+
#事務(wù)A:這個(gè)時(shí)候我們?cè)谑聞?wù)A中能看到數(shù)據(jù)的變化嗎?
select * from tx; --------------->
+------+------+ |
| id | num | |
+------+------+ |
| 1 | 1 |--->并不能看到退渗! |
| 2 | 2 | |
| 3 | 3 | |
+------+------+ |——>相同的select語(yǔ)句,結(jié)果卻不一樣
|
#事務(wù)B:如果提交了事務(wù)B呢? |
commit; |
|
#事務(wù)A: |
select * from tx; --------------->
+------+------+
| id | num |
+------+------+
| 1 | 10 |--->因?yàn)槭聞?wù)B已經(jīng)提交了蕴纳,所以在A中我們看到了數(shù)據(jù)變化
| 2 | 2 |
| 3 | 3 |
+------+------+
第三級(jí)別:Repeatable Read
Repeatable Read会油,當(dāng)前事務(wù)讀取不到其他事務(wù)所做的數(shù)據(jù)更新。事務(wù)執(zhí)行階段內(nèi)某段范圍內(nèi)的數(shù)據(jù)古毛,無(wú)論讀取多少次都是一樣(Repeatable)的翻翩。
該隔離級(jí)別存在另一個(gè)問(wèn)題,幻讀(Phantom Read)
, 是指事務(wù)執(zhí)行期間可能讀取到其他事務(wù)的新增數(shù)據(jù)
的現(xiàn)象稻薇。 例如同一條SQL語(yǔ)句嫂冻,select * from table where id>10 ,第一次執(zhí)行讀出來(lái)5條颖低,第二次執(zhí)行可能讀出來(lái)6條絮吵。
目前,主流的數(shù)據(jù)庫(kù)(Mysql忱屑、Oracle)都通過(guò)多版本并發(fā)控制(MVCC蹬敲,Multiversion Concurrency Control)機(jī)制解決了該問(wèn)題。
#首先莺戒,更改隔離級(jí)別
set tx_isolation='repeatable-read';
select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
#事務(wù)A:?jiǎn)?dòng)一個(gè)事務(wù)
start transaction;
select * from tx;
+------+------+
| id | num |
+------+------+
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
+------+------+
#事務(wù)B:開(kāi)啟一個(gè)新事務(wù)(那么這兩個(gè)事務(wù)交叉了)
在事務(wù)B中更新數(shù)據(jù)伴嗡,并提交
start transaction;
update tx set num=10 where id=1;
select * from tx;
+------+------+
| id | num |
+------+------+
| 1 | 10 |
| 2 | 2 |
| 3 | 3 |
+------+------+
commit;
#事務(wù)A:這時(shí)候即使事務(wù)B已經(jīng)提交了,但A能不能看到數(shù)據(jù)變化?
select * from tx;
+------+------+
| id | num |
+------+------+
| 1 | 1 | --->還是看不到的从铲!(這個(gè)級(jí)別2不一樣瘪校,也說(shuō)明級(jí)別3解決了不可重復(fù)讀問(wèn)題)
| 2 | 2 |
| 3 | 3 |
+------+------+
#事務(wù)A:只有當(dāng)事務(wù)A也提交了,它才能夠看到數(shù)據(jù)變化
commit;
select * from tx;
+------+------+
| id | num |
+------+------+
| 1 | 10 |
| 2 | 2 |
| 3 | 3 |
+------+------+
第四級(jí)別:Serializable
Serializable,事務(wù)串行執(zhí)行阱扬,每次執(zhí)行事務(wù)都對(duì)相關(guān)的數(shù)據(jù)加鎖(一般來(lái)說(shuō)是共享鎖泣懊,其他事務(wù)可讀不可寫(xiě))。
該級(jí)別雖然不存在前面是三個(gè)級(jí)別提到的 臟讀麻惶、不可重復(fù)讀馍刮、幻讀等問(wèn)題,但同時(shí)由于存在大量的鎖競(jìng)爭(zhēng)窃蹋,會(huì)使得吞吐量下降卡啰。
#首先修改隔離界別
set tx_isolation='serializable';
select @@tx_isolation;
+----------------+
| @@tx_isolation |
+----------------+
| SERIALIZABLE |
+----------------+
#事務(wù)A:開(kāi)啟一個(gè)新事務(wù)
start transaction;
#事務(wù)B:在A沒(méi)有commit之前,這個(gè)交叉事務(wù)是不能更改數(shù)據(jù)的
start transaction;
insert tx values('4','4');
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
update tx set num=10 where id=1;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
總結(jié)
當(dāng)然警没,目前Repeatable Read級(jí)別也通過(guò)MVCC機(jī)制解決了幻讀的現(xiàn)象匈辱。
MVCC機(jī)制可以在不需要使用鎖的情況下完成并發(fā)讀寫(xiě),具體實(shí)現(xiàn)機(jī)制待更新杀迹。(▽)