一、最簡單的例子來說明事務
“A賬戶向B賬號匯錢”來說明事務
1、從A賬號中把余額讀出來。
2、對A賬號做減法操作。
3、把結果寫回A賬號中。
4、從B賬號中把余額讀出來。
5、對B賬號做加法操作。
6、把結果寫回B賬號中。
為了數據的一致性,這6件事,要么都成功做完,要么都不成功。而且這個操作的過程中。對A、B找好的其他訪問必須鎖死,所謂鎖死就是要排除其他的讀寫操作,不然會有臟數據問題,這就是事務。
二、數據庫事務特性
-
Atomic(原子性)
事務中包含的操作被看做一個邏輯單元,這個邏輯單元中的操作要么全部成功,要么全部失敗。事務的原子性也體現在事務對數據的讀取上,例如一個事務對同一數據項的多次讀取的結果一定是相同的。
-
Consistency(一致性)
只有合法的數據可以被寫入數據庫,否則事務應該將其回滾到最初的狀態。
事務需要保持數據庫的正確性、完整性和一致性。
有些時候這種一致性由數據庫的內部規則保證,例如數據的類型必須正確,數據值必須在規定的范圍內,等等。
另外一些時候這種一致性由應用保證的,例如,** 一般情況下銀行賬務余額不能是負數,信用卡消費不能超過該卡的信用額度等。** -
Isolation(隔離性)
事務允許多個用戶對同一個數據進行并發訪問,而不破壞數據的正確性和完整性。
同時,并行事務的修改必須與其他并行事務的修改相互獨立。
事務的隔離性一般由事務的鎖來進行控制。許多時候數據庫在并發執行多個事務,每個事務可能需要對多個表進行修改和查詢,與此同時,更多的查詢請求可能要在執行。數據庫需要保證每一個事務在它的修改全部完成之前,對其他的事務是不可見的。
換句話說,不能讓其他事務看到該事務的中間狀態,例如,從銀行賬戶A轉一比款項a到賬戶B,不能讓其他事務(例如賬戶查詢)看到A賬戶已經扣除款項a但B賬戶卻沒有增加款項a的狀態。
-
Durability(持久性)
事務結束后,事務處理的結果必須能夠得到固化,即使系統出現各種異常也是如此。
三、數據庫的隔離級別
由于性能的考慮,許多數據庫允許使用犧牲隔離屬性來換取并發度,從而獲取性能的提升。
Read uncommitted(RU):讀取未提交的數據(未授權讀取),即其他事務已經修改單未commit的數據,這是最低的隔離級別。允許臟讀取,但不允許更新丟失。如果一個事務已經開始寫數據,則另外一個數據則不允許同時進行寫操作,但允許其他事務讀此行數據。該隔離級別可以通過“排他寫鎖”實現。
Read committed(RC):允許不可重復讀取,在一個事務中,對同一個項,前面的讀取跟后面的讀取結果可能不一樣。例如第一次讀取時另一個事務的修改還沒有提交,第二次讀取時已經提交了。這可以通過“瞬間共享讀鎖”和“排他寫鎖”實現。讀取數據的事務允許其他事務繼續訪問該行數據,但是未提交的寫事務將會禁止其他事務訪問該行。
Repeatable read(RR):可重復讀取,在一個事務中,對同一個項,前面的讀取跟后面的讀取結果一樣。這可以通過“共享讀鎖”和“排他寫鎖”實現。讀取數據的事務將會禁止寫事務(但允許讀事務),寫事務則禁止任何其他事務。
Serializable(S):可序列化,即數據庫的事務市可串行化執行的,就像一個事務執行的時候沒有別的事務同時在執行,這是最高的隔離級別。它要求事務序列化執行,事務只能一個接著一個地執行,但不能并發執行。如果僅僅通過“行級鎖”是無法實現事務序列化的,必須通過其他機制保證新插入的數據不會被剛執行查詢操作的事務訪問到。
隔離級別的降低可能導致讀取到臟數據或事務執行異常
Lost update(LU) :兩個事務同時修改一個數據項,但后一個事務中途失敗退出,則對數據項的兩個修改可能都丟失。
Dirty Reads(DR): 一個事務讀取某數據項,但另一個事務更新了此數據項卻沒有提交,這樣所有的操作可能都得回滾。
Non-repeatable Reads(BRR):一個事務對同一數據項的多次讀取可能得到不同的結果。
Second lost updates problem(SLU):無法重復讀取的特例,兩個并發事務同時讀取和修改同一數據項,則后面的修改可能使得前面的修改失效。
Phantom Reads(PR):也稱為幻讀,例如在事務執行過程中,由于前面的查詢和后面的查詢的期間有另外一個事務插入數據,后面的查詢結果中未出現的數據。
隔離級別與讀寫異常(不一致)的關系如下
LU | DR | NRR | SLU | PR | |
---|---|---|---|---|---|
RU | Y | Y | Y | Y | Y |
RC | N | N | Y | Y | Y |
RR | N | N | N | N | Y |
S | N | N | N | N | N |
四、數據庫的事務與線程相似的地方
- 線程之間共享同一片資源,而事務共享的則是數據庫內部的數據
- 多線程的意義在于并發執行,提高效率;事務并發執行也能提高程序與數據庫交互的效率。
五、用例子來說明事務隔離性
假設賬戶A有1000元,B有1000元,C有1000A
1、操作員u1執行一次轉賬事務m1
從A轉移500元到B,再從A的余額中轉移50%元平均分配到 A B C D E余額中
2、操作員u2執行一次轉賬事務m2
從B轉移1000元到A
3、操作員u3執行一次轉賬事務m3
從A轉移200元到B
4、操作員u4開戶D
賬戶表為T_C,其包含字段為 賬戶名稱cname 余額money
記錄為{A,1000},{B,1000}
事務m1的操作包括
讀A,讀B,
寫A,寫B,
提交AB,讀A,
讀C,寫C,寫C,
提交AC
事務m2的操作包括
讀B,讀A,
寫B,寫A,
提交AB
事務m3的操作包括
讀A,讀B,
寫A,寫B,
提交AB
事務m4的操作包括
寫D,
提交D
1.若未授權讀取ReadUncommitted
m1讀A
,B
,寫了A
但沒寫B
此時m2不可以寫B,可以讀取A
和B
,但是B
是臟讀。
隔離級別使用了“排他寫鎖”。
2.若授權讀取ReadCommitted
m1讀A
,B
,寫了A
但沒寫B
此時m2不可以寫B
,可以讀取A
,不能讀取B
,因為B
是臟讀。
隔離級別使用了“排他寫鎖”。
m1讀寫了A
,B
,提交A``B
,
m3提交了A``B
此時m1準備第二次A是允許的。
隔離級別使用了“瞬間共享讀鎖”。
(但由于第二次讀產生了不可重復讀的問題,事務1脫力了元自行,因為邏輯上看事務1中被插入了3,影響了A的余額50%的計算。)
3.若可重復讀取RepeatableRead
m1讀A
,B
,寫了A
。
但沒寫B
此時m2不可以寫B
,可以讀取A
,不能讀取B
,因為B
是臟讀。
隔離級別使用了“排他寫鎖”。
m1讀寫了A
,B
,提交A``B
,
m4提交了D
此時m3是能讀不能寫A
并更新提交的。
此時m4是能讀能能插入D
的。隔離級別使用了“共享讀鎖”。
(和ReadCommitted比RepeatableRead區別對已經提交的事務可以進行讀,但不能寫,但是同一張表可以插入新的記錄。)
4.序列化Serializable
任何事務都只能等前一事務完全執行完再執行。 但是失去了并發性。
轉載,有修改,我只是個搬運工。