自從有了併發,原有的程序哪兒哪兒都很差了——小白語錄。
java
併發事務所致使的問題:spring
當同一個應用程序或者不一樣應用程序中的多個事務在同一個數據集上併發執行時, 可能會出現許多意外的問題sql
併發事務所致使的問題能夠分爲下面三種類型:數據庫
髒讀: 對於兩個事物 T1, T2, T1 讀取了已經被 T2 更新但 尚未被提交的字段. 以後, 若 T2 回滾, T1讀取的內容就是臨時且無效的.併發
不可重複讀:對於兩個事物 T1, T2, T1 讀取了一個字段, 而後 T2 更新了該字段. 以後, T1再次讀取同一個字段, 值就不一樣了.app
幻讀:對於兩個事物 T1, T2, T1 從一個表中讀取了一個字段, 而後 T2 在該表中插入了一些新的行. 以後, 若是 T1 再次讀取同一個表, 就會多出幾行.框架
(本文出自:http://my.oschina.net/happyBKs/blog/513441)性能
事務的隔離級別優化
從理論上來講, 事務應該彼此徹底隔離, 以免併發事務所致使的問題. 然而, 那樣會對性能產生極大的影響, 由於事務必須按順序運行. spa
在實際開發中, 爲了提高性能, 事務會以較低的隔離級別運行.
事務的隔離級別能夠經過隔離事務屬性指定
事務的隔離級別要獲得底層數據庫引擎的支持, 而不是應用程序或者框架的支持.
Oracle 支持的 2 種事務隔離級別:READ_COMMITED , SERIALIZABLE
Mysql 支持 4 種事務隔離級別.
Read Uncommitted(讀取未提交內容)
在該隔離級別,全部事務均可以看到其餘未提交事務的執行結果。本隔離級別不多用於實際應用,由於它的性能也不比其餘級別好多少。讀取未提交的數據,也被稱之爲髒讀(Dirty Read)。
Read Committed(讀取提交內容)
這是大多數數據庫系統的默認隔離級別(但不是MySQL默認的)。它知足了隔離的簡單定義:一個事務只能看見已經提交事務所作的改變。這種隔離級別 也支持所謂的不可重複讀(Nonrepeatable Read),由於同一事務的其餘實例在該實例處理其間可能會有新的commit,因此同一select可能返回不一樣結果。
Repeatable Read(可重讀)
這是MySQL的默認事務隔離級別,它確保同一事務的多個實例在併發讀取數據時,會看到一樣的數據行。不過理論上,這會致使另外一個棘手的問題:幻讀 (Phantom Read)。簡單的說,幻讀指當用戶讀取某一範圍的數據行時,另外一個事務又在該範圍內插入了新行,當用戶再讀取該範圍的數據行時,會發現有新的「幻影」 行。InnoDB和Falcon存儲引擎經過多版本併發控制(MVCC,Multiversion Concurrency Control)機制解決了該問題。
Serializable(可串行化)
這是最高的隔離級別,它經過強制事務排序,使之不可能相互衝突,從而解決幻讀問題。簡言之,它是在每一個讀的數據行上加上共享鎖。在這個級別,可能致使大量的超時現象和鎖競爭。
設置隔離事務屬性
用 @Transactional 註解聲明式地管理事務時能夠在 @Transactional 的 isolation 屬性中設置隔離級別.
在 Spring 2.x 事務通知中, 能夠在 <tx:method> 元素中指定隔離級別
設置回滾事務屬性
默認狀況下只有未檢查異常(RuntimeException和Error類型的異常)會致使事務回滾. 而受檢查異常不會.
事務的回滾規則能夠經過 @Transactional 註解的 rollbackFor 和 noRollbackFor 屬性來定義. 這兩個屬性被聲明爲 Class[] 類型的, 所以能夠爲這兩個屬性指定多個異常類.
rollbackFor: 遇到時必須進行回滾
noRollbackFor: 一組異常類,遇到時必須不回滾
在 Spring 2.x 事務通知中, 能夠在 <tx:method> 元素中指定回滾規則. 若是有不止一種異常, 用逗號分隔.
設置超時和只讀事務屬性
超時和只讀屬性能夠在 @Transactional 註解中定義.超時屬性以秒爲單位來計算.
在 Spring 2.x 事務通知中, 超時和只讀屬性能夠在 <tx:method> 元素中進行指定.
咱們在昨天的例子中將買東西事務的方法進行了相關設置,包括事務隔離級別、只讀屬性、超時timeout
package com.happBKs.spring.tx; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; @Service("bookShopService") public class BookShopServiceImpl implements BookShopService { @Autowired private BookShopDao bookShopDao; //添加事務註解 //使用propagation指定事務的傳播行爲,即當前事務的方法被另外一個事務方法調用時,如何使用是事務。 //使用另一個方法的事務(REQUIRED)?仍是另開一個新事物?(REQUIRES_NEW) //默認REQUIRED //1. REQUIRES_NEW:事務本身的事務,調用該事物方法的事務被掛起 // //2. 用propagation指定事務的隔離級別,最經常使用的是READ_COMMITTED //3. 使用readOnly指定事務是否爲只讀,表示這個事務只讀取數據,不更新數據,這樣能夠幫助數據庫引擎優化事務。 //若是真的是一個只讀取數據庫值的方法,應該設置readOnly=true //4. 使用timeout設置一個時間限額,單位爲秒,若是事務的執行時間超過這個時間閾值,事務將被強制回滾。 @Transactional(propagation=Propagation.REQUIRES_NEW, isolation=Isolation.READ_COMMITTED, readOnly=false, timeout=5 ) public void purchase(String username, String isbn) { // TODO Auto-generated method stub //1. 獲取書的單價 int price=bookShopDao.findBookPriceIsdn(isbn); //2. 更新書的庫存 bookShopDao.updateBookStock(isbn); //3. 更新用戶餘額 bookShopDao.updateUserAccount(username, price); } }