數據庫事務ACID特性
數據庫事務正確執行的4個基礎要素是原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)和持久性(Durability)。
•原子性:整個事務中的所有操作,要么全部完成,要么全部不完成,不可能停滯在中間某個環節。事務在執行過程中發生錯誤,會被回滾到事務開始前的狀態,就像這個事務從來沒被執行過一樣。
•一致性:指一個事務可以改變封裝狀態(除非它是一個只讀的)。事務必須始終保持系統處於一致的狀態,不管在任何給定的時間並發事務有多少。
•隔離性:它是指兩個事務之間的隔離程度。
•持久性:在事務完成以后,該事務對數據庫所做的更改便持久保存在數據庫之中,並不會被回滾。
這里的原子性、一致性和持久性都比較好理解,而隔離性就不一樣了,它涉及了多個事務並發的狀態。首先多個事務並發會產生數據庫丟失更新的問題,其次隔離性又分為多個層級。
•原子性:整個事務中的所有操作,要么全部完成,要么全部不完成,不可能停滯在中間某個環節。事務在執行過程中發生錯誤,會被回滾到事務開始前的狀態,就像這個事務從來沒被執行過一樣。
•一致性:指一個事務可以改變封裝狀態(除非它是一個只讀的)。事務必須始終保持系統處於一致的狀態,不管在任何給定的時間並發事務有多少。
•隔離性:它是指兩個事務之間的隔離程度。
•持久性:在事務完成以后,該事務對數據庫所做的更改便持久保存在數據庫之中,並不會被回滾。
這里的原子性、一致性和持久性都比較好理解,而隔離性就不一樣了,它涉及了多個事務並發的狀態。首先多個事務並發會產生數據庫丟失更新的問題,其次隔離性又分為多個層級。
隔離級別
隔離級別可以在不同程度上減少丟失更新,那么對於隔離級別數據庫標准是怎么定義的呢?按照SQL的標准規范(還有些人認為這是Spring或者Java的規范,而事實是SQL的規范,Spring或者Java只是按照SQL的規范定義的而已),把隔離級別定義為4層,分別是:臟讀(dirty read)、讀/寫提交(readcommit)、可重復讀(repeatable read)和序列化(Serializable)。
臟讀是最低的隔離級別,其含義是允許一個事務去讀取另一個事務中未提交的數據。以丟失更新的消費為例進行說明,如表所示。
臟讀是最低的隔離級別,其含義是允許一個事務去讀取另一個事務中未提交的數據。以丟失更新的消費為例進行說明,如表所示。


由於在T3時刻老婆啟動了消費,導致余額為9 000元,老公在T4時刻消費,因為用了臟讀,所以能夠讀取老婆消費的余額(注意,這個余額是事務二未提交的)為9 000元,這樣余額就為8 000元了,於是T5時刻老公提交事務,余額變為了8 000元,老婆在T6時刻回滾事務,由於數據庫克服了第一類丟失更新,所以余額依舊為8 000元,顯然這是一個錯誤的余額,產生這個錯誤的根源來自於T4時刻,也就是事務一可以讀取事務二未提交的事務,這樣的場景被稱為臟讀。
為了克服臟讀,SQL標注提出了第二個隔離級別—— 讀/寫提交。所謂讀/寫提交,就是說一個事務只能讀取另一個事務已經提交的數據。依舊以丟失更新的夫妻消費為例,如表所示。


在T3時刻,由於事務采取讀/寫提交的隔離級別,所以老公無法讀取老婆未提交的9 000元余額,他只能讀到余額為10 000元,所以在消費后余額依舊為9 000元。在T5時刻提交事務,而T6時刻老婆回滾事務,所以結果為正確的9 000元,這樣就消除了臟讀帶來的問題,但是也會引發其他的問題,如表所示。


由於T7時刻事務一知道事務二提交的結果——余額為1 000元,導致老公無錢買單的尷尬。對於老公而言,他並不知道老婆做了什么事情,但是賬戶余額卻莫名其妙地從10 000元變為了1000元,對他來說賬戶余額是不能重復讀取的,而是一個會變化的值,這樣的場景我們稱為 不可重復讀(unrepeatable read),這是讀/寫提交存在的問題。
為了克服不可重復讀帶來的錯誤,SQL標准又提出了一個 可重復讀的隔離級別來解決問題。注意,可重復讀這個概念是針對數據庫同一條記錄而言的,換句話說,可重復讀會使得同一條數據庫記錄的讀/寫按照一個序列化進行操作,不會產生交叉情況,這樣就能保證同一條數據的一致性,進而保證上述場景的正確性。但是由於數據庫並不是只能針對一條數據進行讀/寫操作,在很多場景,數據庫需要同時對多條記錄進行讀/寫,這個時候就會產生下面的情況,如表所示。


老婆在T1查詢到10條記錄,到T4打印記錄時,並不知道老公在T2和T3時刻進行了消費,導致多一條(可重復讀是針對同一條記錄而言的,而這里不是同一條記錄)消費記錄的產生,她會質疑這條多出來的記錄是不是幻讀出來的,這樣的場景我們稱為幻讀(phantom read)。
為了克服幻讀,SQL標准又提出了 序列化的隔離級別。它是一種讓SQL按照順序讀/寫的方式,能夠消除數據庫事務之間並發產生數據不一致的問題。關於各類的隔離級別和產生的現象如表所示。


選擇隔離級別和傳播行為
選擇隔離級別的出發點在於兩點:性能和數據一致性,下面展開論述。
在互聯網應用中,不但要考慮數據庫數據的一致性,而且要考慮系統的性能。一般而言,從臟讀到序列化,系統性能直線下降。因此設置高的級別,比如序列化,會嚴重壓制並發,從而引發大量的線程掛起,直到獲得鎖才能進一步操作,而恢復時又需要大量的等待時間。因此在購物類的應用中,通過隔離級別控制數據一致性的方式被排除了,而對於臟讀風險又過大。在大部分場景下,企業會選擇讀/寫提交的方式設置事務。這樣既有助於提高並發,又壓制了臟讀,但是對於數據一致性問題並沒有解決,后面會詳細討論如何去克服這類問題。對於一般的應用都可以使用@Transactional方法進行配置
代碼清單:使用讀/寫提交隔離級別
代碼清單:使用序列化隔離級別
只是這樣的代碼會使得數據庫的並發能力低下,在搶購商品的場景下出現卡頓的情況,所以在高並發的場景下這段代碼並不適用
在實際工作中,注解@Transactional隔離級別的默認值為Isolation.DEFAULT,其含義是默認的,隨數據庫默認值的變化而變化。因為對於不同的數據庫而言,隔離級別的支持是不一樣的。比如MySQL可以支持4種隔離級別,而默認的是可重復讀的隔離級別。而Oracle只能支持讀/寫提交和序列化兩種隔離級別,默認為讀/寫提交,這些是在工作中需要注意的問題。
在互聯網應用中,不但要考慮數據庫數據的一致性,而且要考慮系統的性能。一般而言,從臟讀到序列化,系統性能直線下降。因此設置高的級別,比如序列化,會嚴重壓制並發,從而引發大量的線程掛起,直到獲得鎖才能進一步操作,而恢復時又需要大量的等待時間。因此在購物類的應用中,通過隔離級別控制數據一致性的方式被排除了,而對於臟讀風險又過大。在大部分場景下,企業會選擇讀/寫提交的方式設置事務。這樣既有助於提高並發,又壓制了臟讀,但是對於數據一致性問題並沒有解決,后面會詳細討論如何去克服這類問題。對於一般的應用都可以使用@Transactional方法進行配置
代碼清單:使用讀/寫提交隔離級別
@Autowired private RoleDao roleDao = null; //設置方法為讀/寫提交的隔離級別 @Transaction(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED) public int insertRole(Role role) { return roleDao.insert(role); }
當然也會有例外,並不是說所有的業務都在高並發下完成,當業務並發量不是很大或者根本不需要考慮的情況下,使用序列化隔離級別用以保證數據的一致性,也是一個不錯的選擇。總之,隔離級別需要根據並發的大小和性能來做出決定,對於並發不大又要保證數據安全性的可以使用序列化的隔離級別,這樣就能夠保證數據庫在多事務環境中的一致性
代碼清單:使用序列化隔離級別
@Autowired private RoleDao roleDao = null; //設置方法為序列化的隔離級別 @Transaction(propagation = Propagation.REQUIRED, isolation = Isolation.SERIALIZABLE) public int insertRole(Role role) { return roleDao.insert(role); }
只是這樣的代碼會使得數據庫的並發能力低下,在搶購商品的場景下出現卡頓的情況,所以在高並發的場景下這段代碼並不適用
在實際工作中,注解@Transactional隔離級別的默認值為Isolation.DEFAULT,其含義是默認的,隨數據庫默認值的變化而變化。因為對於不同的數據庫而言,隔離級別的支持是不一樣的。比如MySQL可以支持4種隔離級別,而默認的是可重復讀的隔離級別。而Oracle只能支持讀/寫提交和序列化兩種隔離級別,默認為讀/寫提交,這些是在工作中需要注意的問題。
傳播行為
傳播行為是指方法之間的調用事務策略的問題。在大部分的情況下,我們都希望事務能夠同時成功或者同時失敗。但是也會有例外
在Spring中傳播行為的類型,是通過一個枚舉類型去定義的,這個枚舉類是org.springframework.transaction.annota-tion.Propagation,它定義了如表所列舉的7種傳播行為。
在Spring中傳播行為的類型,是通過一個枚舉類型去定義的,這個枚舉類是org.springframework.transaction.annota-tion.Propagation,它定義了如表所列舉的7種傳播行為。


文章來源:ssm 13.4,13.5