數據庫基本概念之事務與並發控制
- 事務ACID
- 鎖
參考資料:關系數據庫工作原理簡述
數據庫事務具有ACID特性
「為什么引入原子性」事務執行過程中可能是不一致的狀態,如果能確保事務原子性,除了執行過程中,其它時刻不一致性狀態都是不可見的。通過記錄redo/undo日志-通過
- 原子性(Atomicity):數據庫事務包含的所有操作,要么全部執行成功,要么完全不執行。執行成功的操作寫入數據庫,一旦執行失敗對數據庫沒有任何影響。
- 一致性(Consistency):數據庫在事務執行前和執行后都是一致性的狀態,即事務的執行是從一個一致性狀態轉換至另一個一致性狀態「完整性約束」
- 隔離性(Isolation):數據庫應該保證:事務不應該讀取其他事務產生的不一致的中間數據,因為這樣會導致對數據庫的錯誤更新。即,每個事務都感覺不到其它事務在並發的執行。
- 持久性(Durability):事務執行成功,它對數據庫的改變將是永久的--數據庫發生故障不應該影響已經成功之行的事務。
事務調度
調度:多個事務的中操作的一個CPU執行順序。
可串行化調度:如果存在調度S1,使得無論什么數據庫初態,調度S1、S效果相同,那么S是可串行化的。
兩個事務操作Ii,Ij分別屬於事務Ti,Tj沖突的條件
- 操作Ii,Ij位於不同事務中,即:i!=j
- 操作面向同一個數據對象
- 兩個操作中至少有一個為寫
若調度S經過若干非沖突操作的交換,得到調度S1,那么S和S1是沖突等價的,若S為串行調度則稱S1為沖突可串行化。
調度S與S1稱為視圖可串行化,當滿足如下條件:
- 任意數據項Q,調度S中是Ti讀取了初始值,那么調度S1中也是Ti讀取了初始值
- 任意數據項Q,調度S中Ti讀取了Tj寫入的數據,那么S1中也必須是Ti讀取了Tj寫入的數據
- 任意數據項Q,調度S中Ti完成最后寫,那么S1中也必須是Ti完成最后寫
視圖可串行化比沖突可串行化更寬松一點,任意一個視圖可串行化非沖突可串行化的調度中,肯定存在盲寫(即不讀直接寫)。級聯回滾是由於臟讀引起的,避免級聯回滾讀已提交。如果仍然是讀未提交級別的話,需要滿足當前事務讀的數據的創造者早於。
鎖
為什么要引入鎖?防止多個事務並發執行時:同時對數據庫元素讀/寫導致非可串行化發生。臨界區
如果在訪問數據完成立即釋放鎖,將會導致不一致的情況發生。例如:
T1: X(A)R(A)(A+=50)W(A)U(A)X(B)R(B)(B-=50)W(B)U(B)
T2: S(A)R(A)U(A)S(B)R(B)U(B)display(A+B)
事務執行過程中,SET A、B 100,執行結果可能是250,也可能是150,造成了數據庫的不一致性。
若假定事務約定提交時候才釋放鎖,那么會導致死鎖的產生。例如:
T1: W(A)W(B)
T2: W(B)W(A)
在事務T1執行完W(A),需要在B上加X鎖才能釋放在A上的鎖,T2需要在A上加X鎖才能釋放B上的排他鎖,這就導致一個死鎖的發生。Ti->Tj表示Tj等待Ti持有的鎖,假如等待圖中出現環,即表示發生死鎖,需回滾其中一個事務才能解決。
餓死: T1持有A上的S鎖,T2要在A上加X鎖,如果再有事務在A上加S鎖,都會排在T2之前,就會導致T2總是不能加排他鎖。避免的條件(T要在Q上加M鎖):
- 不存在其它事務持有Q上與M沖突的鎖
- 等待隊列中不存在先於T的事務(先后順序)
避免死鎖的措施:T2需要在Q上加鎖,如果Q的等待隊列中存在T1,T1需要持有T2已持有的鎖,那么把T2放在T1前邊。
- 兩階段封鎖(two phase lock,2PL):即在釋放鎖之前必須把其他的鎖都加上。是實現可串行化的充分條件。
- 嚴格兩階段封鎖協議:事務持有的排他鎖在事務執行結束才釋放
- 強兩階段封鎖協議:事務提交之前不得釋放任何鎖
更新鎖:兩個事務先加共享鎖,然后兩個事務都想升級鎖,導致的經典死鎖問題,更新鎖加鎖時自己和S一樣,已持有后,更排他鎖一樣
警示鎖:用在數據庫元素多層次上,IS IX S X對子元素上鎖是,必須先對父元素上鎖
幻讀
:將insert、delete設計成對數據庫表粒度的寫操作
T1
BEGIN
select count(*) from table1 where id = 1;
***** <--
end;
begin;
insert into table(id) values(1);
end;
以上例子T1首先對表中所有元組加S鎖,正在計算還未提交,事務T2插入了一條符號條件的,但是被漏掉了~
樹形結構封鎖:例子,索引文件的索引塊
- 事務第一個鎖可以在樹的任意節點上
- 只有事務當前節點的父節點持有鎖時才能獲得后續的鎖
- 節點可以任意時候釋放鎖
- 事務不能對一個解鎖的事務重新上鎖,即使改節點在其父節點上仍然持有鎖(不能回頭)
有效性確認:
- 對於所有經過有效性驗證且在T開始前沒有完成的U,即滿足FIN(U)>START(T),檢測WS(T) WS(U)是否有交叉。
- 對於已經經過有效性驗證且在T有效性驗證前沒有完成的U,