spring 事務(wù)管理
事務(wù)
事務(wù)是應(yīng)用程序中一系列嚴(yán)密的操作,所有操作必須成功完成,否則在每個(gè)操作中所作的所有更改都會(huì)被撤消。也就是事務(wù)具有原子性,一個(gè)事務(wù)中的一系列的操作要么全部成功,要么一個(gè)都不做。 事務(wù)的結(jié)束有兩種,當(dāng)事務(wù)中的所以步驟全部成功執(zhí)行時(shí),事務(wù)提交。如果其中一個(gè)步驟失敗,將發(fā)生回滾操作,撤消撤消之前到事務(wù)開始時(shí)的所以操作。就像從小父母教育我們要有始有終,不能半途而廢。
事務(wù)的ACID
事務(wù)具有四個(gè)特征:原子性( Atomicity )、一致性( Consistency )、隔離性( Isolation )和持續(xù)性( Durability )。這四個(gè)特性簡稱為 ACID 特性。
1 、原子性
事務(wù)是數(shù)據(jù)庫的邏輯工作單位,事務(wù)中包含的各操作要么都做,要么都不做
2 、一致性
事務(wù)執(zhí)行的結(jié)果必須是使數(shù)據(jù)庫從一個(gè)一致性狀態(tài)變到另一個(gè)一致性狀態(tài)。因此當(dāng)數(shù)據(jù)庫只包含成功事務(wù)提交的結(jié)果時(shí),就說數(shù)據(jù)庫處于一致性狀態(tài)。如果數(shù)據(jù)庫系統(tǒng) 運(yùn)行中發(fā)生故障,有些事務(wù)尚未完成就被迫中斷,這些未完成事務(wù)對(duì)數(shù)據(jù)庫所做的修改有一部分已寫入物理數(shù)據(jù)庫,這時(shí)數(shù)據(jù)庫就處于一種不正確的狀態(tài),或者說是不一致的狀態(tài)。
比如轉(zhuǎn)賬:A轉(zhuǎn)給B5000塊,A的賬號(hào)扣了錢,但是B的賬號(hào)沒有增加。
3 、隔離性
一個(gè)事務(wù)的執(zhí)行不能其它事務(wù)干擾。即一個(gè)事務(wù)內(nèi)部的操作及使用的數(shù)據(jù)對(duì)其它并發(fā)事務(wù)是隔離的,并發(fā)執(zhí)行的各個(gè)事務(wù)之間不能互相干擾。比如我們?cè)趗pdata的那一剎那,有可能別人提交了一個(gè)delete對(duì)于同一個(gè)數(shù)據(jù)進(jìn)行操作??梢韵胂筮@樣會(huì)出麻煩,必須加以控制,讓他們順序的執(zhí)行,鎖進(jìn)行控制。保證數(shù)據(jù)庫操作之間是隔離的,沒有干擾的。隔離的級(jí)別越高,那么并發(fā)性,吞吐量肯定會(huì)收到影響。數(shù)據(jù)庫專家們制定了數(shù)據(jù)庫隔離的規(guī)范,防止臟讀,不可重復(fù)讀..
4 、持續(xù)性
也稱永久性,指一個(gè)事務(wù)一旦提交,它對(duì)數(shù)據(jù)庫中的數(shù)據(jù)的改變就應(yīng)該是永久性的。接下來的其它操作或故障不應(yīng)該對(duì)其執(zhí)行結(jié)果有任何影響。
事務(wù)隔離面臨的影響
臟讀:事務(wù)A讀取了事務(wù)B未提交的數(shù)據(jù)
不可重復(fù)讀:事務(wù)A讀取了事務(wù)B已提交更改的數(shù)據(jù)。
幻讀:事務(wù)A讀取了事務(wù)B已提交新增的數(shù)據(jù)。
事務(wù)隔離性的定義:Isolation: Concurrently executing transactions see the stored information as if they were running serially (one after another)
事務(wù)的隔離級(jí)別從低到高有:
Read Uncommitted:最低的隔離級(jí)別,什么都不需要做,一個(gè)事務(wù)可以讀到另一個(gè)事務(wù)未提交的結(jié)果。所有的并發(fā)事務(wù)問題都會(huì)發(fā)生。
Read Committed:只有在事務(wù)提交后,其更新結(jié)果才會(huì)被其他事務(wù)看見??梢越鉀Q臟讀問題。
Repeated Read:在一個(gè)事務(wù)中,對(duì)于同一份數(shù)據(jù)的讀取結(jié)果總是相同的,無論是否有其他事務(wù)對(duì)這份數(shù)據(jù)進(jìn)行操作,以及這個(gè)事務(wù)是否提交??梢越鉀Q臟讀、不可重復(fù)讀。
Serialization:事務(wù)串行化執(zhí)行,隔離級(jí)別最高,犧牲了系統(tǒng)的并發(fā)性??梢越鉀Q并發(fā)事務(wù)的所有問題。 通常,在工程實(shí)踐中,為了性能的考慮會(huì)對(duì)隔離性進(jìn)行折中。
考慮到實(shí)踐,為了性能,數(shù)據(jù)庫廠商做出了這方面的妥協(xié),讓使用者可以選擇隔離的級(jí)別。不同的隔離級(jí)別可以解決不同階段的問題,是層層遞進(jìn),逐漸增強(qiáng)的關(guān)系。
隔離性為了解決的問題主要有三個(gè)(將事務(wù)的隔離級(jí)別和問題聯(lián)系在一起理解):
臟讀(Drity Read):事務(wù)A修改了一個(gè)數(shù)據(jù),但未提交,事務(wù)B讀到了事務(wù)A未提交的更新結(jié)果,如果事務(wù)A提交失敗,事務(wù)B讀到的就是臟數(shù)據(jù)。Read Committed可以解決臟讀問題,但仍存在以下兩種問題。
不可重復(fù)讀(Non-repeatable read): 在同一個(gè)事務(wù)中,對(duì)于同一份數(shù)據(jù)讀取到的結(jié)果不一致。比如,事務(wù)B在事務(wù)A提交前讀到的結(jié)果,和提交后讀到的結(jié)果可能不同。不可重復(fù)讀出現(xiàn)的原因就是事務(wù)并發(fā)修改記錄,要避免這種情況,最簡單的方法就是對(duì)要修改的記錄加鎖,這導(dǎo)致鎖競爭加劇,影響性能。Repeated Read可以解決不可重復(fù)讀問題和臟讀問題,但仍無法解決下面的問題。
幻讀(Phantom Read): 在同一個(gè)事務(wù)中,同一個(gè)查詢多次返回的結(jié)果不一致。事務(wù)A新增了一條記錄,事務(wù)B在事務(wù)A提交前后各執(zhí)行了一次查詢操作,發(fā)現(xiàn)后一次比前一次多了一條記錄?;米x僅指由于并發(fā)事務(wù)增加記錄導(dǎo)致的問題,這個(gè)不能像不可重復(fù)讀通過記錄加鎖解決,因?yàn)閷?duì)于新增的記錄根本無法加鎖。需要將事務(wù)串行化,才能避免幻讀。Serialization解決了以上所有問題,但是性能效率較低。通常來說,事務(wù)隔離級(jí)別越低,所需持有鎖的時(shí)間也就越短,并發(fā)性能也就越好。
不可重復(fù)讀的例子:
不可重復(fù)讀的重點(diǎn)是修改:同樣的條件, 你讀取過的數(shù)據(jù), 再次讀取出來發(fā)現(xiàn)值不一樣了
在事務(wù)1中,Mary 讀取了自己的工資為1000,操作并沒有完成
con1 = getConnection();
select salary from employee empId = "Mary" ;
在事務(wù)2中,這時(shí)財(cái)務(wù)人員修改了Mary的工資為2000,并提交了事務(wù).
con2 = getConnection();
update employee set salary = 2000 ;
con2.commit();
在事務(wù)1中,Mary 再次讀取自己的工資時(shí),工資變?yōu)榱?000
select salary from employee empId = "Mary" ;
在一個(gè)事務(wù)中前后兩次讀取的結(jié)果并不致,導(dǎo)致了不可重復(fù)讀。
幻讀例子:
幻讀:幻讀的重點(diǎn)在于新增或者刪除 同樣的條件, 第1次和第2次讀出來的記錄數(shù)不一樣
目前工資為1000的員工有10人。
事務(wù)1,讀取所有工資為1000的員工。
con1 = getConnection();
Select * from employee where salary = 1000 ;
共讀取10條記錄
這時(shí)另一個(gè)事務(wù)向employee表插入了一條員工記錄,工資也為1000
con2 = getConnection();
Insert into employee(empId,salary) values( "Lili" , 1000 );
con2.commit();
事務(wù)1再次讀取所有工資為1000的員工
select * from employee where salary = 1000 ;
共讀取到了11條記錄,這就產(chǎn)生了幻像讀。
spring 事務(wù)的隔離級(jí)別(方法間調(diào)用)
ISOLATION_DEFAULT:數(shù)據(jù)庫默認(rèn)
ISOLATION_COMMITTED:允許讀取其他并發(fā)事務(wù)已經(jīng)提交的更新(防此臟讀)
ISOLATION—READ_UNCOMMITTED允許讀取其他并發(fā)事務(wù)還未提交的更新,會(huì)導(dǎo)致事務(wù)之間的3個(gè)缺陷發(fā)生,這是速度最快的一個(gè)隔離級(jí)別,但同時(shí)它的隔離級(jí)別也是最低
ISOLATION_SERIALIZABLE 這是最高的隔離級(jí)別,它可以防此臟讀,不可重復(fù)讀和 幻讀等問題,但因其侵占式的數(shù)據(jù)記錄完全鎖定,導(dǎo)致它影響事務(wù)的性能,成為隔離級(jí)別中最展慢的一個(gè)。 注意:并不是所有的資源管理器都支持所有的隔離級(jí)別,可針對(duì)不同的資源管理使用以上的隔離級(jí)別.
事務(wù)的傳播行為
PROPAGATION_MANDATORY: (強(qiáng)制性,傳播)
規(guī)定了方法必須在事務(wù)中運(yùn)行,否則會(huì)拋出異常
PROPAGATION_NEVER(調(diào)用的方法不能有事務(wù))
使當(dāng)前方法永遠(yuǎn)不在事務(wù)中運(yùn)行,否則拋出異常
PROPAGATION-NOT_SUPPORTED
定義為當(dāng)前事務(wù)不支持的方法,在該方法運(yùn)行期間正在運(yùn)行的事務(wù)會(huì)被暫停
PROPAGATION_REQUIRED(夠用)
規(guī)定當(dāng)前的方法必須在事務(wù)中,如果沒有事務(wù)就創(chuàng)建一個(gè)新事務(wù),一個(gè)新事務(wù)和方法一同開始,隨著方法的返回或拋出異常而終止
PROPAGATION-REQUIRED_NEW
當(dāng)前方法必須創(chuàng)建新的事務(wù)來運(yùn)行,如果現(xiàn)存的事務(wù)正在運(yùn)行就暫停它
PROPAGATION_SUPPORTS
規(guī)定當(dāng)前方法支持當(dāng)前事務(wù)處理,但如果沒有事務(wù)在運(yùn)行就使用非事務(wù)方法執(zhí)行
事務(wù)的只讀屬性
在對(duì)數(shù)據(jù)庫的操作中,查詢是使用最頻繁的操作,每次執(zhí)行查詢時(shí)都要從數(shù)據(jù)庫中重新讀取數(shù)據(jù),有時(shí)多次讀取的數(shù)據(jù)都是相同的,這樣的數(shù)據(jù)操作不僅浪費(fèi)了系統(tǒng)資源,還影響了系統(tǒng)速度。對(duì)訪問量大的程序來說,節(jié)省這部分資源可以大大提升系統(tǒng)速度。如果將事務(wù)聲明為只讀的,那么數(shù)據(jù)庫可以根據(jù)事務(wù)的特性優(yōu)化事務(wù)的讀取操作。事務(wù)的只讀屬性需要配合事務(wù)的傳播行為共同設(shè)置.
事務(wù)超時(shí)屬性
這個(gè)屬性和事務(wù)的只讀屬性一樣需要搭配事務(wù)的傳播行為共同設(shè)置,它設(shè)置了事務(wù)的超時(shí)時(shí)間,事務(wù)本身可能會(huì)因某種原因很長沒有回應(yīng),在這期間事務(wù)可能鎖定了數(shù)據(jù)庫的表格,這樣會(huì)出現(xiàn)嚴(yán)重的性能問題。通過設(shè)置事務(wù)的超時(shí)時(shí)間,從開始執(zhí)行事務(wù)起,在規(guī)定的超時(shí)時(shí)間內(nèi)如果沒有事務(wù)就將它回滾。
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
String value() default "";
Propagation propagation() default Propagation.REQUIRED;
Isolation isolation() default Isolation.DEFAULT;
int timeout() default -1;
boolean readOnly() default false;
Class<? extends Throwable>[] rollbackFor() default {};
String[] rollbackForClassName() default {};
Class<? extends Throwable>[] noRollbackFor() default {};
String[] noRollbackForClassName() default {};
}
spring 事務(wù)管理器
都實(shí)現(xiàn)了PlatformTransactionManager接口
DataSourceTransactionManager JDBC事務(wù)管理器
HibernateTransactionManager Hibernate事務(wù)管理器
<!-- 配置JDBC事務(wù)管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource">
<ref bean="dataSource"/>
</property>
</bean>
<!-- 配置Hibernate事務(wù)管理器 -->
<bean id="transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
啟動(dòng)注解,@Transactional
<tx:annotation-driven transaction-manager="transactionManager"/>
使用AOP的方式實(shí)現(xiàn)事務(wù)的配置
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
<!--配置事務(wù)傳播性,隔離級(jí)別以及超時(shí)回滾等問題 -->
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" />
<tx:method name="del*" propagation="REQUIRED" />
<tx:method name="update*" propagation="REQUIRED" />
<tx:method name="add*" propagation="REQUIRED" />
<tx:method name="*" rollback-for="java.lang.Exception" timeout="-1" isolation="READ_COMMITTED" read-only="true" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<!--配置事務(wù)切點(diǎn) -->
<aop:pointcut id="services"
expression="execution(* com.website.service.*.*(..))" />
<aop:advisor pointcut-ref="services" advice-ref="TestAdvice" />
</aop:config>
本文摘自 :https://blog.51cto.com/u