嬰兒衣服做的網(wǎng)站域名注冊(cè)管理機(jī)構(gòu)
事務(wù)概述
事務(wù)是數(shù)據(jù)庫(kù)區(qū)別于文件系統(tǒng)的重要特性之一,當(dāng)我們有了事務(wù)就會(huì)讓數(shù)據(jù)庫(kù)始終保持一致性,同時(shí)我們還能通過事務(wù)機(jī)制恢復(fù)到某個(gè)時(shí)間點(diǎn),這樣可以保證已提交到數(shù)據(jù)庫(kù)的修改不會(huì)因?yàn)橄到y(tǒng)崩潰而丟失。
1、基本概念
事務(wù):一組邏輯操作單元,是數(shù)據(jù)庫(kù)從一種狀態(tài)變換到另一種狀態(tài)。
事務(wù)處理的機(jī)制:保證所有的事務(wù)都作為一個(gè)工作單元來執(zhí)行,即使出項(xiàng)了故障,都不能改變這種執(zhí)行方式。當(dāng)在一個(gè)事務(wù)中執(zhí)行多個(gè)操作時(shí),要么所有的事務(wù)都提交commit,那么這些修改就永久的保存下來;要么數(shù)據(jù)庫(kù)管理系統(tǒng)將放棄所作的所有修改,整個(gè)事務(wù)回滾rollback到最初狀態(tài)
#案例:AA用戶給BB用戶轉(zhuǎn)賬100
update account set money = money - 100 where name = 'AA';
update account set money = money + 100 where name = 'BB';
2、事務(wù)的ACID特性
- 原子性(atomicity)
原子性是指事務(wù)是一個(gè)不可分割的工作單元,要么全部提交,要么全部失敗回滾。既要么轉(zhuǎn)賬成功,要么轉(zhuǎn)賬失敗,是不存在中間的狀態(tài)。如果無法保證原子性會(huì)怎么樣?就會(huì)出項(xiàng)數(shù)據(jù)不一致的情形,A賬戶減去100元,而B賬戶增加100元操作失敗,系統(tǒng)將無故丟失100元。
- 一致性(consistency)
一致性是指事務(wù)執(zhí)行前后,數(shù)據(jù)從一個(gè)合法性狀態(tài)變換到另外一個(gè)合法性狀態(tài)。這種狀態(tài)是語義上的而不是語法上的,跟具體的業(yè)務(wù)有關(guān)。
那什么是合法的數(shù)據(jù)狀態(tài)呢?滿足預(yù)定的約束的狀態(tài)就叫做合法的狀態(tài)。通俗一點(diǎn),這狀態(tài)是由你自己來定義的(比如滿足現(xiàn)實(shí)世界中的約束)。滿足這個(gè)狀態(tài),數(shù)據(jù)就是一致性的,不滿足這個(gè)狀態(tài),數(shù)據(jù)就是不一致的!如果事務(wù)中的某個(gè)操作失敗了,系統(tǒng)就會(huì)自動(dòng)撤銷當(dāng)前正在執(zhí)行的事務(wù),返回到事務(wù)操作之前的狀態(tài)。
舉例1:A賬戶有200元,轉(zhuǎn)賬300元出去,此時(shí)A賬戶余額為-100元。你自然就發(fā)現(xiàn)了此時(shí)數(shù)據(jù)是不一致的,為什么呢?因?yàn)槟愣x了一個(gè)狀態(tài),余額這列必須>=0。
舉例2:A賬戶有200元,轉(zhuǎn)賬50元給B賬戶,A賬戶的錢扣了,但是B賬戶因?yàn)楦鞣N意外,余額并沒有增加。你也知道此時(shí)數(shù)據(jù)是不一致的,為什么呢?因?yàn)槟愣x了一個(gè)狀態(tài),要求A+B的總余額 必須不變。
- 隔離性(isolation)
事務(wù)的隔離性是指一個(gè)事務(wù)的執(zhí)行不能被其他事務(wù)干擾,即一個(gè)事務(wù)內(nèi)部的操作及使用的數(shù)據(jù)對(duì)并發(fā)的其他事務(wù)是隔離的,并發(fā)執(zhí)行的各個(gè)事務(wù)之間不能互相干擾。
如果無法保證隔離性會(huì)怎么樣?假設(shè)A賬戶有200元,B賬戶0元,A賬戶往B賬戶轉(zhuǎn)賬兩次,每次金額為50元,分別在兩個(gè)事務(wù)中執(zhí)行。如果無法保證隔離性,會(huì)出項(xiàng)下面的情形:
UPDATE accounts SET money = money - 50 where NAME = 'AA';UPDATE accounts SET money = money + 50 where NAME = 'BB';
事務(wù)1 | 事務(wù)2 |
---|---|
從磁盤上將A賬戶余額讀取到變量A中 A為200 | |
執(zhí)行操作A=A-50 | |
將A的值寫回磁盤 A為150 | |
從磁盤中將B賬戶余額讀取到變量B中 B為0 | |
執(zhí)行操作B=B+50 | |
從磁盤上將A賬戶余額讀取到變量A中 A為150 | |
執(zhí)行操作A=A-50 | |
將A的值寫回磁盤 A為100 | |
從磁盤中將B賬戶余額讀取到變量B中 B為0 | |
執(zhí)行操作B=B+50 | |
將B值寫回磁盤 B為50 | |
將B值寫回磁盤 B為50 |
- 持久性(durability)
持久性是指一個(gè)事務(wù)一旦被提交,它對(duì)數(shù)據(jù)庫(kù)中數(shù)據(jù)的改變就是永久性的,接下來的其他操作的數(shù)據(jù)庫(kù)故障不應(yīng)該對(duì)其他有任何影響。
持久性是通過事務(wù)日志來保證的。日志包括了重做日志和回滾日志。當(dāng)我們通過事務(wù)對(duì)數(shù)據(jù)進(jìn)行修改的時(shí)候,首先會(huì)將數(shù)據(jù)庫(kù)的變化信息記錄到重做日志中,然后再對(duì)數(shù)據(jù)庫(kù)中對(duì)應(yīng)的行進(jìn)行修改。這樣中的好處是,即使數(shù)據(jù)庫(kù)系統(tǒng)崩潰,數(shù)據(jù)庫(kù)重啟后也能找到?jīng)]有更新到數(shù)據(jù)庫(kù)系統(tǒng)中的重做日志,重新執(zhí)行,從而使事務(wù)具有持久性。
總結(jié):ACID是事務(wù)的四大特性,在這四個(gè)特性中,原子性是基礎(chǔ),隔離性是手段,一致性是約束條件,而持久性是我們的目的。數(shù)據(jù)庫(kù)事務(wù),其實(shí)就是數(shù)據(jù)庫(kù)設(shè)計(jì)者為了方便起見,把需要保證的原子性,隔離性,一致性和持久性的一個(gè)或多個(gè)數(shù)據(jù)庫(kù)操作稱為一個(gè)事務(wù)。
3、數(shù)據(jù)并發(fā)問題
針對(duì)事務(wù)的隔離性和并發(fā)性,我們?cè)趺醋鋈∩崮?#xff1f;先看一下訪問相同數(shù)據(jù)的事務(wù)在不保證串行執(zhí)行(也就是執(zhí)行完一個(gè)再執(zhí)行另一個(gè))的情況下可能會(huì)出現(xiàn)哪些問題:
1、臟寫(Dirty Write)
對(duì)于兩個(gè)事務(wù)SessionA、SessionB,如果事務(wù)SessionA修改了另一個(gè)未提交事務(wù)SessonB修改過的數(shù)據(jù),那就意味著發(fā)生了臟寫,如圖所示:
發(fā)生時(shí)間編號(hào) | SessionA | SessionB |
---|---|---|
1 | Begin; | |
2 | Begin; | |
3 | UPDATE student SET name = ‘李四’ WHERE studentno = 1; | |
4 | UPDATE student SET name=‘張三’ WHERE studentno = 1; | |
5 | COMMIT; | |
6 | ROLLBACK; |
SessionA和SessionB各開啟了一個(gè)事務(wù),SessionB中的事務(wù)先將studentso列為1的記錄的name更新為‘李四’,然后SessionA中的事務(wù)接著又把這條studentno列為1的記錄的name列更新為‘張三’。如果之后SessionB中的事務(wù)進(jìn)行了回滾,那么SessionA中更新也將不復(fù)存在,這種現(xiàn)象就稱之為臟寫。這時(shí)SessionA中的事務(wù)就沒有效果了,明明把數(shù)據(jù)更新了,最后也提交事務(wù)了,最后看到的數(shù)據(jù)沒有變化。這里大家對(duì)事務(wù)的隔離級(jí)別比較了解的話,會(huì)發(fā)現(xiàn)默認(rèn)隔離級(jí)別下,上面SessionA中的更新語句會(huì)處于等待狀態(tài),這里只是跟大家說明一下會(huì)出現(xiàn)這樣現(xiàn)象。
2、臟讀(Dirty Read)
對(duì)于兩個(gè)事務(wù)SessionA、SessionB,Session A 讀取了已經(jīng)被Session B更新但還沒有被提交的字段。之后若Session B回滾,Session A 讀取的內(nèi)容就是臨時(shí)且無效的。如圖所示:
發(fā)生時(shí)間編號(hào) | SessionA | SessionB |
---|---|---|
1 | Begin; | |
2 | Begin; | |
3 | UPDATE student SET name = ‘張三’ WHERE studentno = 1; | |
4 | SELECT * FROM student WHERE studentno = 1;(如果讀取到列name的值為’張三’,則意味著發(fā)生了臟讀) | |
5 | COMMIT; | |
6 | ROLLBACK; |
SessionA和SeesionB各開啟了一個(gè)事務(wù),SessionB中的事務(wù)先將studentno列為1的記錄的name列更新為‘張三’,然后SessionA中的事務(wù)再去查詢這條studentno為1的記錄,如果讀取到列name的值為’張三‘,而Session B中的事務(wù)稍后進(jìn)行了回滾,那么SessionA中的事務(wù)相當(dāng)于讀取到了一個(gè)不存在的數(shù)據(jù),這種現(xiàn)象就稱之為臟讀。
3、不可重復(fù)讀(Non-Repeatable Read)
對(duì)于兩個(gè)事務(wù)SessionA、SessionB,Session A讀取了一個(gè)字段,然后SessionB更新了該字段,之后Session A再次讀取同一個(gè)字段,值就不同了,那就意味著發(fā)生了不可重復(fù)讀。
發(fā)生時(shí)間編號(hào) | SessionA | SessionB |
---|---|---|
1 | Begin; | |
2 | SELECT * FROM student WHERE studentno = 1;(此時(shí)讀到的列name的值為’王五‘) | |
3 | UPDATE student SET name= ‘張三’ WHERE studentno = 1; | |
4 | SELECT * FROM student WHERE studentno = 1;(如果讀到列name的值為’張三‘,則意味著發(fā)生了不可重復(fù)讀) | |
5 | UPDATE student SET name = ‘李四’ WHERE studentno = 1; | |
6 | SELECT * FROM student WHERE studentno = 1;(如果讀到列name的值為’李四‘,則意味著發(fā)生了不可重復(fù)讀) |
我們?cè)赟ession B中提交幾個(gè)隱式事務(wù)(注意是隱式事務(wù),意味著語句結(jié)束事務(wù)就提交了),這些事務(wù)都修改了studentno列為1的記錄的列name的值,每次事務(wù)提交之后,如果Session A中的事務(wù)都可以查看到最新的值,這種現(xiàn)象就稱之為不可重復(fù)讀。
4、幻讀(Phantom)
對(duì)于兩個(gè)事務(wù)Session A、Session B,SessionA從一個(gè)表中讀取了一個(gè)字段,然后SessionB在該表中插入 了一些新的行。之后,如果Session A再次讀取同一個(gè)表,就會(huì)出多幾行。那就意味著發(fā)生了幻讀。
發(fā)生時(shí)間編號(hào) | SessionA | SessionB |
---|---|---|
1 | Begin; | |
2 | SELECT * FROM student WHERE studentno>0;(此時(shí)讀取到的列name的值為’張三‘) | |
3 | INSERT INTO student VALUES (2,‘趙六’,‘2班’); | |
4 | SELECT * FROM student WHERE studentno>0;(如果讀取到了列name的值為’張三‘、’趙六‘的記錄,則意味著發(fā)生了幻讀) |
SessionA中的事務(wù)先根據(jù)條件studentno >0這個(gè)條件查詢表student,得到了name的值為’張三‘的記錄;之后SessionB中提交了一個(gè)隱式事務(wù),該事務(wù)向表student中插入了一條新的記錄;之后SessionA中的事務(wù)再根據(jù)相同的條件studentno > 0 查詢表student,得到的結(jié)果集中包含Session B中的事務(wù)新插入的那條記錄,這種現(xiàn)象也被稱之為幻讀。我們把新插入的那些記錄稱之為幻影記錄。
注意1:
有的同學(xué)會(huì)產(chǎn)生疑問,那如果SessionB中刪除了一些符合studentno > 0的記錄而不是插入新記錄,那Session A之后再根據(jù)studentno > 0的條件讀取的記錄變少了,這種現(xiàn)象算不算幻讀呢?這種現(xiàn)象不屬于幻讀,幻讀強(qiáng)調(diào)的是一個(gè)事務(wù)按照某個(gè)相同條件多次讀取記錄時(shí),后讀取時(shí)讀到了之前沒有讀到的記錄。
注意2:
那對(duì)于先前已經(jīng)讀到的記錄,之后又讀取不到這種情況,算啥呢?這相當(dāng)于對(duì)每一條記錄都發(fā)生了不可重復(fù)讀的現(xiàn)象?;米x只是重點(diǎn)強(qiáng)調(diào)了讀取到了之前讀取沒有獲取到的記錄。
4、SQL中的四種隔離級(jí)別
上面介紹了幾種并發(fā)事務(wù)執(zhí)行過程中可能遇到的一些問題,這些問題有輕重緩急之分,我們給這些問題按照嚴(yán)重性來排一下序:
臟寫 > 臟讀 > 不可重復(fù)讀 > 幻讀
我們?cè)敢馍崛ヒ徊糠指綦x性來換取一部分性能在這里就體現(xiàn)在:設(shè)立一些隔離級(jí)別,隔離級(jí)別越低,并發(fā)問題發(fā)生的就越多。SQL標(biāo)準(zhǔn)中設(shè)立了4個(gè)隔離級(jí)別:
- READ UNCOMMITTED:讀未提交,在該隔離級(jí)別,所有事務(wù)都可以看到其他未提交事務(wù)的執(zhí)行結(jié)果。不能避免臟讀,不可重復(fù)讀,幻讀。
- READ COMMITTED:讀已提交,它滿足了隔離的簡(jiǎn)單定義:一個(gè)事務(wù)只能看見已經(jīng)提交事務(wù)所做的改變。這是大多數(shù)數(shù)據(jù)庫(kù)系統(tǒng)的默認(rèn)隔離級(jí)別(但不是MySQL默認(rèn)的)??梢员苊馀K讀,但不可重復(fù)讀,幻讀問題仍然存在。
- REPEATABLE READ:可重復(fù)讀,事務(wù)A在讀到一條數(shù)據(jù)之后,此時(shí)事務(wù)B對(duì)該數(shù)據(jù)進(jìn)行了修改并提交,那么事務(wù)A再讀該數(shù)據(jù),讀到的還是原來的內(nèi)容。可以避免臟讀,不可重復(fù)讀,但幻讀問題仍然存在。這是MySQL的默認(rèn)隔離級(jí)別。
- SERIALIZABLE:可串行化,確保事務(wù)可以從一個(gè)表中讀取相同的行。在這個(gè)事務(wù)持續(xù)期間,禁止其他事務(wù)對(duì)該表執(zhí)行插入,更新和刪除操作。所有的并發(fā)問題都可以避免,但性能十分底下。能避免臟讀、不可重復(fù)讀和幻讀。
SQL標(biāo)準(zhǔn)中規(guī)定,針對(duì)不同的隔離級(jí)別,并發(fā)事務(wù)可以發(fā)生不同嚴(yán)重程度的問題,具體情況如下:
隔離級(jí)別 | 臟讀可能性 | 不可重復(fù)讀可能性 | 幻讀可能性 | 加鎖讀 |
---|---|---|---|---|
READ UNCOMMITTED | Yes | Yes | Yes | No |
READ COMMITTED | No | Yes | Yes | No |
REPEATABLE READ | No | No | Yes | No |
SERIALIZABLE | No | No | No | Yes |
Yes:表示會(huì)出現(xiàn)的可能性,No:表示不會(huì)出現(xiàn)的可能性。
臟寫沒有涉及到?因?yàn)榕K寫這個(gè)問題太嚴(yán)重了,不論是哪種隔離級(jí)別,都不允許臟寫的情況發(fā)生。
不同的隔離級(jí)別有不同的現(xiàn)象,并有不同的鎖和并發(fā)機(jī)制,隔離級(jí)別越高,數(shù)據(jù)庫(kù)的并發(fā)性能就越差。