數據庫事務 ACID屬性、數據庫並發問題和四種隔離級別
數據庫事務
數據庫事務是一組邏輯操作單元,使數據從一種狀態變換到另一種狀態
一組邏輯操作單元;一個或多個DML操作
事務處理原則
保證所有事務都作為一個工作單元來執行,即使出現故障,都不能改變這種執行方式。
一個事務執行多次操作時,要么所有事務都被提交,則永久保存;要么放棄所有修改,整個事務回滾到最初狀態
數據一旦提交,則不可回滾
那些操作會導致自動提交
DDL操作一旦執行,都會自動提交
DML默認情況下,一旦執行,就會自動提交
可以通過set autocommit = false的方式取消DML自動提交
默認在關閉連接時,會自動提交數據
ACID屬性
事務必須滿足四個屬性,即原子性(Atomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability),即ACID四種屬性。
原子性
一個事務是一個不可分割的整體,為了保證事務的總體目標,事務必須具有原子性,即當數據修改時,要么全都執行,要么全都不執行。即,不允許事務部分地完成,避免了只執行這些操作的一部分而帶來的錯誤。
一致性
一個事務在執行之前和執行之后,數據庫數據必須保持一致性。數據庫的一致性狀態應該滿足模式鎖指定的約束條件,那么在完整執行該事務后,數據庫仍然處於一致性狀態。
例如:銀行轉賬,轉賬前后兩個賬戶金額之和應保持不變。
隔離性
由並發事務所作的修改必須與任何其它並發事務所作的修改隔離。事務查看數據庫時數據所處的狀態,要么是另一並發事務修改它之前的狀態,要么是另一事務修改它之后的狀態,事務不會查看中間狀態的數據。
例如:對任何一對事務T1和T2,對T1而言,T2要么在T1開始之前已經結束,要么在T1完成之后再開始執行。
持久性
也被稱為永久性,事務完成以后,DBMS(數據庫管理系統)保證它對數據庫中數據的修改是永久性的,當系統或介質發生故障時,該修改也永久保持。持久性一般通過數據庫備份與恢復來保證。
- 注意 嚴格而言,數據庫事務屬性都是由數據庫管理系統來進行保證的,在整個應用程序的運行過程中,應用程序無須去考慮數據庫的ACID實現。
一般情況下,通過執行COMMIT(提交)或ROLLBACK(回滾)語句來終止事務。當執行COMMIT語句時,自從事務啟動以來對數據庫所做的一切更改就成為永久性的,即被寫入到磁盤,而當執行ROLLBACK語句時,自從事務啟動以來對數據庫所做的一切更改都會被撤銷,並且數據庫中內容返回到事務開始之前所處的狀態。無論什么情況,在事務完成時,都能保證回到一致性狀態。
數據庫並發問題
如果沒有鎖定且多個用戶同時訪問一個數據庫,則當他們的事務同時使用相同的數據時可能會發生問題。由於並發操作帶來的數據不一致性包括:丟失數據更新、讀“臟”數據(臟讀)、不可重復讀。
更新丟失
- 兩個事務都同時更新一行數據,一個事務對數據的更新把另一個事務對數據的更新覆蓋了。這是因為系統沒有執行任何的鎖操作,因此並發並沒有被隔離開來。
臟讀
- 一個事務讀取到了另一事務未提交的數據操作結果。這是相當危險的,因為很可能所有的操作都被回滾。
不可重復讀
- 不可重復讀(Non-repeatable Reads):一個事務對同一行數據重復讀取兩次,但是卻得到了不同的結果。
包括以下情況:
- 虛讀:事務T1讀取某一數據后,事務T2對其做了修改,當事務T1再次讀取該數據時得到與前一次不同的值。
- 幻讀:事務在操作過程中進行兩次查詢,第二次查詢的結果包含了第一次查詢中未出現的數據或者缺少了第一次查詢中出現的數據。這是因為在兩次查詢過程中有另外一個事務插入數據造成的。
數據庫事務的四種隔離級別
數據庫事務的隔離級別有4個,由低到高依次為Read uncommitted 、Read committed 、Repeatable read 、Serializable ,這四個級別可以逐個解決臟讀 、不可重復讀 、幻讀這幾類問題。
不同的隔離級別對事務的處理不同。
- 讀未提交數據(Read Uncommitted):只處理更新丟失。如果一個事務已經開始寫數據,則不允許其他事務同時進行寫操作,但允許其他事務讀此行數據。可通過“排他寫鎖”實現。
- 讀已提交數據(Read Committed):處理更新丟失、臟讀。讀取數據的事務允許其他事務繼續訪問改行數據,但是未提交的寫事務將會禁止其他事務訪問改行。可通過“瞬間共享讀鎖”和“排他寫鎖”實現。
- 可重復讀取(Repeatable Read):處理更新丟失、臟讀和不可重復讀取。讀取數據的事務將會禁止寫事務,但允許讀事務,寫事務則禁止任何其他事務。可通過“共享讀鎖”和“排他寫鎖”實現。
- 序列化(串行化)(Serializable):提供嚴格的事務隔離。要求失去序列化執行,事務只能一個接一個地執行,不能並發執行。僅僅通過“行級鎖”是無法實現事務序列化的,必須通過其他機制保證新插入的數據不會被剛執行查詢操作的事務訪問到。
隔離級別越高,越能保證數據的完整性和統一性,但是對並發性能的影響也越大。對於多數應用程序,可以優先考慮把數據庫系統的隔離級別設為Read Committed。它能夠避免臟讀,而且具有較好的並發性能。盡管它會導致不可重復讀、幻讀和第二類丟失更新這些並發問題,在可能出現這類問題的個別場合,可以由應用程序采用悲觀鎖或樂觀鎖來控制。
- Oracle 支持的 2 種事務隔離級別:READ COMMITED, SERIALIZABLE. Oracle 默認的事務隔離級別為: READ COMMITED
- Mysql 支持 4 中事務隔離級別. Mysql 默認的事務隔離級別為: REPEATABLE READ
事務隔離級別設置
//查看當前事物級別:
SELECT @@tx_isolation;
//設置mysql的隔離級別:
//set session transaction isolation level 設置事務隔離級別
//設置read uncommitted級別:
set session transaction isolation level read uncommitted;
//設置read committed級別:
set session transaction isolation level read committed;
//設置repeatable read級別:
set session transaction isolation level repeatable read;
//設置serializable級別:
set session transaction isolation level serializable;
Java代碼獲取和設置隔離級別(下文conn為Connection 對象,具體實現不做整理)
//獲取數據庫事務隔離級別
int transactionIsolation = conn.getTransactionIsolation();
//設置數據庫事務隔離級別;事務隔離級別:TRANSACTION_READ_COMMITTED
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);