本文參考自知乎
Oracle事務的概念:事務用戶保證數據的一致性,它是由一組dml語句組成,這組dml語句要么全部執行成功,要么全部執行失敗。
1、事務一致性
舉個例子:假如你去銀行轉1000元給你的朋友,所有的操作都完成之后,並且提示你轉賬成功(假設銀行是立即轉賬,不存在延時的情況),你發現你的賬戶上減少了1000元,但是你打電話給你的朋友確認時,而你的朋友的賬戶卻沒有因此增加1000元,那么我們認為這時候的數據就是不一致的狀態!!!
在數據庫的實現的應用場景中,一致性可以分為數據庫外部的一致性和數據庫內部的一致性:
i、外部的一致性:由外部的應用編碼來實現,即銀行的應用在進行轉賬的操作時,必須在同一事務內部調用對賬戶A和賬戶B的操作。如果在這個階段出現錯誤,這不是數據庫本身能解決的,也不屬於我們要討論的范圍。
ii、數據庫內部的一致性:在同一個事物內部的一組操作必須全部成功(或者全部失敗)。這就是事物處理的原子性
2、事務原子性
上面說了事務的原子性是保證:事務內的一組操作全部成功(或者全部失敗),為了實現原子性,就需要通過日志:將所有對數據的操作都寫入日志,如果事務中的一部分操作已經成功,但后面部分操作,因為系統斷電,操作系統崩潰等問題而沒有成功執行,那么就要通過回溯日志,將前面已經成功執行的操作撤銷,從而達到"全部執行失敗"的效果。
3、體現事務原子性和數據庫一致性和持久性的常見場景
數據庫崩潰后重啟,此時數據庫處於不一致的狀態,此時數據庫必須做crash recovery操作,大致步驟如下:
a、通過日志REDO(重演所有執行成功但是未寫入到磁盤的操作)
b、再對到數據庫崩潰前沒有執行完成的事務進行UNDO(撤銷所有執行了一部分,但是有一部份還沒有執行完成,且尚未提交的操作,保證事務的原子性)
c、crash recovery結束后,數據庫恢復了一致性,可以繼續工作
4、多線程下的事務存在的問題
在單線程下,事務的原子性,能保證數據庫的一致性,但是在某些情況下,事務的原子性並不能保證數據庫的一致性。具體場景如下:
問題:事務一將100元轉到帳號A,那么他先讀取帳號A,然后再在帳號A上加上100,但是在這個過程中間,事務二也修改了帳號A,給它加了100元,那么最后的結果應該是加了200元。但是當事務一最終完成后,帳號A只加了100,因為事務二的修改結果被事務一覆蓋掉了。
為了保證數據的一致性,引入隔離性,既保證每一個事務看到的數據是一致的,確保一個事務在處理數據的同時,沒有其他事務對自己正在處理的數據進行干擾,就好像其他事務都是不存在的一樣,即事務在並發執行后的狀態,和串行執行后的狀態時一樣的,實現隔離性主要通過加鎖的方式。
下面是通過"鎖"解決事務在多線程下的數據不一致性問題:
a、悲觀鎖
即事務將當前操作所有涉及到的對象加鎖,操作完成后釋放給其他對象使用,為了盡可能的提高性能,發明了各種粒度(數據庫級/表級/行級)/各種性質(共享鎖/排它鎖/共享意向鎖/共享排它鎖/共享排他意向鎖......)的鎖,想具體了解鎖,請參考Oracle鎖機制。為了解決死鎖問題,又發明了兩階段鎖協議/死鎖檢測等一系列技術
b、樂觀鎖
即不同事務看到通一對象(一般是數據行)的不同歷史版本,如果有兩個事務同時修改了同一數據行,那么在較晚提交的事務提交使進行沖突檢測。實現也有兩種:一種是通過UNDO的方式獲取數據行的歷史版本,另一種是簡單的在內存中保存數據行的不同歷史版本,通過時間戳來區分