目前項目開發中都是基於Spring+MyBatis的,各類配置已經封裝好,能夠進行快速開發,不須要關心底層的實現。可是在線上出現問題時,若是對於Java事務底層的機制不是很熟悉的話,會很難分析並定位出來,在這裏對Java事務進行分類整理,並不斷的完善。數據庫
1、數據庫事務概念數據結構
結合《數據庫系統概念》一書和百度百科,數據庫事務的基本概念以下:多線程
(1)數據庫事務(Database Transaction) 概念:併發
是指做爲單個邏輯工做單元執行的一系列操做,要麼徹底地執行,要麼徹底地不執行,是一個不可分割的工做單位。 事務處理能夠確保除非事務性單元內的全部操做都成功完成,不然不會永久更新面向數據的資源。經過將一組相關操做組合爲一個要麼所有成功要麼所有失敗的單元,能夠簡化錯誤恢復並使應用程序更加可靠。一個邏輯工做單元要成爲事務,必須知足所謂的ACID(原子性、一致性、隔離性和持久性)屬性。事務是數據庫運行中的邏輯工做單位,由DBMS中的事務管理子系統負責事務的處理。性能
(2)數據庫事務的週期:ui
事務一般是以begin transaction開始,以commit或rollback結束。Commint表示提交,即提交事務的全部操做。具體地說就是將事務中全部對數據的更新寫回到磁盤上的物理數據庫中去,事務正常結束。Rollback表示回滾,即在事務運行的過程當中發生了某種故障,事務不能繼續進行,系統將事務中對數據庫的全部已完成的操做所有撤消,滾回到事務開始的狀態。spa
事務必須是原子工做單元;對於其數據修改,要麼全都執行,要麼全都不執行。一般,與某個事務關聯的操做具備共同的目標,而且是相互依賴的。若是系統只執行這些操做的一個子集,則可能會破壞事務的整體目標。原子性消除了系統處理操做子集的可能性。.net
事務在完成時,必須使全部的數據都保持一致狀態。在相關數據庫中,全部規則都必須應用於事務的修改,以保持全部數據的完整性。事務結束時,全部的內部數據結構(如 B 樹索引或雙向鏈表)都必須是正確的。某些維護一致性的責任由應用程序開發人員承擔,他們必須確保應用程序已強制全部已知的完整性約束。例如,當開發用於轉賬的應用程序時,應避免在轉賬過程當中任意移動小數點。線程
由併發事務所做的修改必須與任何其它併發事務所做的修改隔離。事務查看數據時數據所處的狀態,要麼是另外一併發事務修改它以前的狀態,要麼是另外一事務修改它以後的狀態,事務不會查看中間狀態的數據。這稱爲隔離性,由於它可以從新裝載起始數據,而且重播一系列事務,以使數據結束時的狀態與原始事務執行的狀態相同。當事務可序列化時將得到最高的隔離級別。在此級別上,從一組可並行執行的事務得到的結果與經過連續運行每一個事務所得到的結果相同。因爲高度隔離會限制可並行執行的事務數,因此一些應用程序下降隔離級別以換取更大的吞吐量。日誌
事務完成以後,它對於它對數據庫的修改被永久保持,該修改即便出現致命的系統故障也將一直保持。事務日誌可以保持事務的永久性。
一個事務讀取了另外一個事務還沒有提交的數據
例如:事務A、B併發執行時,當A事務update後,B事務select讀取到A還沒有提交的數據,此時A事務rollback,則B讀到的數據是無效的"髒"數據。
一個事務的操做致使另外一個事務先後兩次讀取到不一樣的數據
例如:當B事務select讀取數據後,A事務update操做更改B事務select到的數據,此時B事務再次讀去該數據,發現先後兩次的數據不同。
一個事務的操做致使另外一個事務先後兩次查詢的結果數據量不一樣。
例如:當B事務select讀取數據後,A事務insert或delete了一條知足A事務的select條件的記錄,此時B事務再次select,發現查詢到前次不存在的記錄("幻影"),或者前次的某個記錄不見了。
從以上事務併發的分析中能夠看出,當事務併發進行的時候,可能會出現髒讀、不可重複讀、幻讀等問題,相似於Java 多線程併發,若是徹底採用串行化的話,系統的開銷會很大,併發度不高,若是採用較低力度的控制時,可能達不到某些業務場景的要求。所以,SQL標準中定義了四種隔離級別,每一種級別都規定了一個事務中所做的修改,在哪些事務內和事務間是可見的,哪些是不可見的。較低級別的隔離一般能夠執行更高的併發,系統的開銷更低。四種隔離級別以下:
READ UNCOMMITTED (未提交讀):
在未提交讀級別中,事務中的修改,即便沒有提交,對其餘事務也都是可見的。事務能夠讀取未提交的數據,這也被稱爲髒讀(Dirty Read)。這個級別會致使不少問題,從性能上來講 ,READ UNCOMMITTED不會比其餘的級別好太多,但卻缺少其餘級別的不少好處,除非真的有很是必要的理由,在實際應用中通常不多使用。
READ COMMITTED (提交讀):
大多數數據庫系統的默認隔離級別就是READ COMMITTED(但MYSQL 不是)。READ COMMITTED知足前面提到的隔離性的簡單定義:一個事務開始時,只能「看見」已經提交的事務所作的修改。換句話說,一個事務從開始知道提交前,所作的任何修改對其它事務都是不可見的。這個級別有時候也叫做不可重複讀(nonrepeatable read),由於兩次執行一樣的查詢,可能會獲得不同的結果。
REPEATABLE READ(可重複讀):
可重複讀解決了髒讀的問題。該級別保證了在同一個事務中屢次讀取一樣的記錄的結果是一致的。可是理論上,可重複讀隔離級別仍是沒法解決另一個幻讀(Phantom Read)的問題。所謂幻讀,指的是當某個事務在讀取某個範圍內的記錄時,另一個事務又在該範圍內插入了新的記錄,當以前的事務再次讀取改範圍內的記錄時,會產生幻行(Phantom Row)。InnoDB和XtraDB存儲引擎經過多版本併發控制(MVCC,Multiversion Concurrency Control)解決了幻讀的問題。可重複讀是MySQL默認的事務隔離級別。
SERIALIZABLE(可串行化):
可串行化是最高的隔離級別。它經過強制事務串行執行,避免了前面說的幻讀的問題。簡單來講,可串行化會在讀取的每一行數據上都加鎖,因此可能致使大量的超時和鎖競爭的問題。實際應用中也不多用到這個隔離級別,只有在很是須要確保數據的一致性並且能夠接受沒有併發的狀況下,才考慮採用該級別。
Java事務機制主要是基於JDBC的Connection:
Connection提供了一個auto-commit的屬性來指定事務什麼時候結束,默認是true。
a、當auto-commit爲true時,當每一個獨立SQL操做的執行完畢,事務當即自動提交,也就是說每一個SQL操做都是一個事務。JDBC規範是這樣規定一個獨立SQL操做何時算執行完畢的:
i)、對數據操做語言(DML,如insert,update,delete)和數據定義語言(如create,drop),語句一執行完就視爲執行完畢。
ii)、對select語句,當與它關聯的ResultSet對象關閉時,視爲執行完畢。
iii)、對存儲過程或其餘返回多個結果的語句,當與它關聯的全部ResultSet對象所有關閉,全部update count(update,delete等語句操做影響的行數)和output parameter(存儲過程的輸出參數)都已經獲取以後,視爲執行完畢。
b. 當auto-commit爲false時,每一個事務都必須顯示調用commit方法進行提交,或者顯示調用rollback方法進行回滾。
JDBC提供了5種不一樣的事務隔離級別(Transaction Isolation Levels),在Connection中進行了定義:
TRANSACTION_NONE JDBC驅動不支持事務
TRANSACTION_READ_UNCOMMITTED 容許髒讀、不可重複讀和幻讀。
TRANSACTION_READ_COMMITTED 禁止髒讀,但容許不可重複讀和幻讀。
TRANSACTION_REPEATABLE_READ 禁止髒讀和不可重複讀,單運行幻讀。
TRANSACTION_SERIALIZABLE 禁止髒讀、不可重複讀和幻讀。
代碼以下:
/** * A constant indicating that transactions are not supported. */ int TRANSACTION_NONE = 0; /** * A constant indicating that * dirty reads, non-repeatable reads and phantom reads can occur. * This level allows a row changed by one transaction to be read * by another transaction before any changes in that row have been * committed (a "dirty read"). If any of the changes are rolled back, * the second transaction will have retrieved an invalid row. */ int TRANSACTION_READ_UNCOMMITTED = 1; /** * A constant indicating that * dirty reads are prevented; non-repeatable reads and phantom * reads can occur. This level only prohibits a transaction * from reading a row with uncommitted changes in it. */ int TRANSACTION_READ_COMMITTED = 2; /** * A constant indicating that * dirty reads and non-repeatable reads are prevented; phantom * reads can occur. This level prohibits a transaction from * reading a row with uncommitted changes in it, and it also * prohibits the situation where one transaction reads a row, * a second transaction alters the row, and the first transaction * rereads the row, getting different values the second time * (a "non-repeatable read"). */ int TRANSACTION_REPEATABLE_READ = 4; /** * A constant indicating that * dirty reads, non-repeatable reads and phantom reads are prevented. * This level includes the prohibitions in * <code>TRANSACTION_REPEATABLE_READ</code> and further prohibits the * situation where one transaction reads all rows that satisfy * a <code>WHERE</code> condition, a second transaction inserts a row that * satisfies that <code>WHERE</code> condition, and the first transaction * rereads for the same condition, retrieving the additional * "phantom" row in the second read. */ int TRANSACTION_SERIALIZABLE = 8;
JDBC定義了SavePoint接口,提供在一個更細粒度的事務控制機制。當設置了一個保存點後,能夠rollback到該保存點處的狀態,而不是rollback整個事務。Connection接口的setSavepoint和releaseSavepoint方法能夠設置和釋放保存點。
JDBC規範雖然定義了事務的以上支持行爲,可是各個JDBC驅動,數據庫廠商對事務的支持程度可能各不相同。若是在程序中任意設置,可能得不到想要的效果。爲此,JDBC提供了DatabaseMetaData接口,提供了一系列JDBC特性支持狀況的獲取方法。好比,經過DatabaseMetaData.supportsTransactionIsolationLevel方法能夠判斷對事務隔離級別的支持狀況,經過DatabaseMetaData.supportsSavepoints方法能夠判斷對保存點的支持狀況。
參考鏈接:http://blog.csdn.net/huilangeliuxin/article/details/43446177
參考連接:http://blog.csdn.net/sinat_33536912/article/details/51200630