事務具備最重要的兩個特性:Spring事務的傳播級別和數據庫事務的隔離級別。傳播級別定義控制範圍,隔離級別定義數據庫的讀寫等方面。spring
Spring事務的傳播級別:數據庫
1. PROPAGATION_REQUIRED: 若是存在一個事務,則支持當前事務。若是沒有事務則開啓,。適用與大多數場景。緩存
2. PROPAGATION_SUPPORTS: 若是存在一個事務,支持當前事務。若是沒有事務,則非事務的執行。 併發
3. PROPAGATION_MANDATORY: 若是已經存在一個事務,支持當前事務。若是沒有一個活動的事務,則拋出異常。 框架
4. PROPAGATION_REQUIRES_NEW: 老是開啓一個新的事務。若是一個事務已經存在,則將這個存在的事務掛起。新建事務完成以後再恢復執行。 性能
5. PROPAGATION_NOT_SUPPORTED: 老是非事務地執行,並掛起任何存在的事務。 spa
6. PROPAGATION_NEVER: 老是非事務地執行,若是存在一個活動事務,則拋出異常 rest
7. PROPAGATION_NESTED:若是一個活動的事務存在,則運行在一個嵌套的事務中, 若是沒有活動事務,則按TransactionDefinition.PROPAGATION_REQUIRED 屬性執行。orm
數據庫事務的隔離級別:xml
事務的隔離級別是基於併發狀況下操做數據遇到的異常而言的。這些異常狀況有:
一、 Dirty reads---讀髒數據:也就是說,事務A的未提交(還依然緩存)的數據被事務B讀走,若是事務A失敗回滾,會致使事務B所讀取的的數據是錯誤的。
二、 non-repeatable reads---數據不可重複讀:好比事務A中兩處讀取數據-total-的值。在第一讀的時候,total是100,而後事務B就把total的數據改爲200,事務A再讀一次,結果就發現,total居然就變成200了,形成事務A數據混亂。
三、 phantom reads---幻讀數據:和non-repeatable reads類似,也是同一個事務中屢次讀不一致的問題。可是non-repeatable reads的不一致是由於他所要取的數據集被改變了(好比total的數據)
四、 Lost update---更新丟失:兩個事務都同時更新一行數據可是第二個事務卻中途失敗退出致使對數據兩個修改都失效了這是系統沒有執行任何鎖操做所以併發事務並無被隔離開來.
五、 Second lost updates problem---兩次更新:沒法重複讀取的特例,有兩個併發事務同時讀取同一行數據,而後其中一個對它進行修改提交而另外一個也進行了修改提交,這就會形成第一次的寫操做失效
對於解決以上問題,數據庫設置了隔離級別相應的來處理這些問題:
×:表示解決的問題 √:表示可能引發的問題
各級別詳解:
第1級別:Read Uncommitted (讀取未提交內容)
(1) 全部事務均可以看到其餘未提交事務的執行結果
(2) 本隔離級別不多用於實際應用,由於它的性能也不比其餘級別好多少
(3) 該級別引起的問題是——髒讀(Dirty Read):讀取到了未提交的數據
#首先,修改隔離級別 set tx_isolation='READ-UNCOMMITTED'; select @@tx_isolation; +-------------------------------+ | @@tx_isolation | +-------------------------------+ | READ-UNCOMMITTED | +-------------------------------+
|
#事務A:啓動一個事務 start transaction; select * from tx; +---------+--------+ | id | num | +---------+--------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +---------+--------+ |
#事務B:也啓動一個事務,在事務B中執行更新語句,不提交 start transaction; update tx set num=10 where id=1; select * from tx; +--------+--------+ | id | num | +---------+--------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +---------+--------+ |
#事務A:那麼這時候事務A能看到這個更新了的數據嗎? select * from tx; +--------+--------+ | id | num | +---------+------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +->能夠看到,說明咱們讀到了事務B尚未提交的數據 |
#事務B:事務B回滾,仍然未提交 rollback; select * from tx; +--------+--------+ | id | num | +---------+---------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +----------+--------+ |
#事務A:在事務A裏面看到的也是B沒有提交的數據 select * from tx; +---------+--------+ | id | num | +---------+--------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +-->髒讀意味着我在這個事務中(A中),事務B雖然沒有提交,但它任何一條數據變化,我均可以看到! |
第2級別:Read Committed(讀取提交內容)
(1) 這是大多數數據庫系統的默認隔離級別(但不是MySQL默認的)
(2) 它知足了隔離的簡單定義:一個事務只能看見已經提交事務所作的改變
(3) 這種隔離級別出現的問題是——不可重複讀(Nonrepeatable Read):不可重複讀意味着咱們在同一個事務中執行徹底相同的select語句時可能看到不同的結果。
致使這種狀況的緣由可能有:①有一個交叉的事務有新的commit,致使了數據的改變; ②一個數據庫被多個實例操做時,同一事務的其餘實例在該實例處理其間可能會有新的commit。
#修改隔離級別 set tx_isolation='read-committed'; select @@tx_isolation; +--------------------------+ | @@tx_isolation | +--------------------------+ | READ-COMMITTED | +---------------------------+ |
#事務A:啓動一個事務 start transaction; select * from tx; +---------+---------+ | id | num | +---------+---------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +---------+--------+ |
#事務B:也啓動一個事務,在這事務中更新數據,不提交 start transaction; update tx set num=10 where id=1; select * from tx; +---------+---------+ | id | num | +---------+---------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +---------+---------+ |
第3級別:Repeatable Read(可重讀)
(1) 這是MySQL的默認事務隔離級別;
(2) 它確保同一事務的多個實例在併發讀取數據時,會看到一樣的數據行;
(3) 此級別可能出現的問題——幻讀(Phantom Read):當用戶讀取某一範圍的數據行時,另外一個事務又在該範圍內插入了新行,當用戶再讀取該範圍的數據行時,會發現有新的「幻影」行;
(4) InnoDB和Falcon存儲引擎經過多版本併發控制(MVCC,Multiversion Concurrency Control)機制解決了該問題。
#更改隔離級別 set tx_isolation='repeatable-read'; select @@tx_isolation; +------------------------+ | @@tx_isolation | +-------------------------+ | REPEATABLE-READ | +-------------------------+ |
#事務A:啓動一個事務 start transaction; select * from tx; +--------+---------+ | id | num | +--------+---------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +--------+---------+ |
#事務B:開啓一個新事務,在事務B中更新數據,並提交 start transaction; update tx set num=10 where id=1; select * from tx; +--------+---------+ | id | num | +--------+---------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +--------+---------+ commit; |
#事務A:這時候即便事務B已經提交了,但A不能看到數據變化 select * from tx; +--------+---------+ | id | num | +--------+---------+ | 1 | 1 | | 2 | 2 | | 3 | 3 | +-> (這個級別2不同,也說明級別3解決了不可重複讀問題) |
#事務A:只有當事務A也提交了,它纔可以看到數據變化 commit; select * from tx; +--------+---------+ | id | num | +--------+---------+ | 1 | 10 | | 2 | 2 | | 3 | 3 | +--------+---------+ |
第4級別:Serializable(可串行化)
(1) 這是最高的隔離級別;
(2) 經過強制事務排序,使之不可能相互衝突,從而解決幻讀問題。它是在每一個讀的數據行上加上共享鎖;
(3) 在這個級別,可能致使大量的超時現象和鎖競爭。併發量大的時候,就會死掉。
#首先修改隔離級別 set tx_isolation='serializable'; select @@tx_isolation; +----------------------+ | @@tx_isolation | +-----------------------+ | SERIALIZABLE | +-----------------------+ |
#事務A:開啓一個新事務 start transaction; |
#事務B:在A沒有commit以前,這個交叉事務是不能更改數據的 start transaction; insert tx values('4','4'); ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction update tx set num=10 where id=1; ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction |
小結:在項目中配置事務------註釋方式
一、配置文件
<!-- 定義事務管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!--使用註釋事務 --> <tx:annotation-driven transaction-manager="transactionManager" /> |
注意:使用不一樣的orm框架,事務管理器類class就不一樣
二、在項目中加@Transactional註釋:
(1)放在類名稱上,這樣配置的@Transactional 對這個類中的全部public方法都起做用;
(2)Transactional 在方法名上,只對這個方法有做用,但必須是public的方法;
三、事物中的屬性配置:
(1)事務的傳播性:@Transactional(propagation=Propagation.REQUIRED)
(2)事務的超時性:@Transactional(timeout=30) //默認是30秒
(3)事務的隔離級別:@Transactional(isolation = Isolation.READ_UNCOMMITTED)
(4)事務:
指定單一異常類:@Transactional(rollbackFor=RuntimeException.class)
指定多個異常類:
@Transactional(rollbackFor={RuntimeException.class, Exception.class})
(5)只讀(true表示只讀,默認false表示可讀寫):ransactional(readOnly=true)
注意:
在spring配置文件中引入<tx:>命名空間: <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context ttp://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd"> |