關(guān)于數(shù)據(jù)庫(kù)事務(wù)的那些事兒

本文約5000字裙品,建議閱讀時(shí)間10分鐘

關(guān)于數(shù)據(jù)庫(kù)的事務(wù)帝火,相信每個(gè)碼農(nóng)都有接觸,也相信都遇到過(guò)與之相關(guān)的坑风罩。
本文旨在歸納總結(jié)下事務(wù)的概念讶踪、原理及使用。
本文針對(duì)的主要是MySQL的事務(wù)機(jī)制以及Spring 的事務(wù)管理泊交。


什么是事務(wù)

簡(jiǎn)單挑明下事務(wù)的概念乳讥,從說(shuō)事務(wù)都會(huì)舉的一個(gè)實(shí)際問(wèn)題引出事務(wù)的概念:
用戶(hù)A想要給用戶(hù)B轉(zhuǎn)賬100元,那么需要做的事情是:

  1. 查詢(xún)A賬戶(hù)的信息
  2. 如大于100廓俭,從A賬戶(hù)中取出100
  3. 查詢(xún)B賬戶(hù)的信息
  4. 如果B賬戶(hù)一切正常云石,往B賬戶(hù)中存入100

上面的流程不要太過(guò)注重細(xì)節(jié),闡述流程用研乒,其中明顯會(huì)有幾個(gè)問(wèn)題汹忠,比如,A雹熬、B賬戶(hù)處于異常狀態(tài)宽菜,不能進(jìn)行交易;A的余額不足100等等
當(dāng)出現(xiàn)問(wèn)題的時(shí)候竿报,這整個(gè)轉(zhuǎn)賬的動(dòng)作都是失敗的铅乡,我們需要保證A賬戶(hù)的錢(qián)不能少,B賬戶(hù)的錢(qián)也不能多烈菌,都還是原來(lái)的金額阵幸。
自此便是事務(wù)的概念:
數(shù)據(jù)庫(kù)事務(wù)(簡(jiǎn)稱(chēng):事務(wù))是數(shù)據(jù)庫(kù)管理系統(tǒng)執(zhí)行過(guò)程中的一個(gè)邏輯單位,由一個(gè)有限的數(shù)據(jù)庫(kù)操作序列構(gòu)成芽世。
一個(gè)數(shù)據(jù)庫(kù)事務(wù)通常包含了一個(gè)序列的對(duì)數(shù)據(jù)庫(kù)的讀/寫(xiě)操作挚赊。它的存在包含有以下兩個(gè)目的:

  1. 為數(shù)據(jù)庫(kù)操作序列提供了一個(gè)從失敗中恢復(fù)到正常狀態(tài)的方法,同時(shí)提供了數(shù)據(jù)庫(kù)即使在異常狀態(tài)下仍能保持一致性的方法济瓢。
  2. 當(dāng)多個(gè)應(yīng)用程序在并發(fā)訪問(wèn)數(shù)據(jù)庫(kù)時(shí)荠割,可以在這些應(yīng)用程序之間提供一個(gè)隔離方法,以防止彼此的操作互相干擾旺矾。

當(dāng)事務(wù)被提交給了DBMS蔑鹦,則DBMS需要確保該事務(wù)中的所有操作都成功完成且其結(jié)果被永久保存在數(shù)據(jù)庫(kù)中,如果事務(wù)中有的操作沒(méi)有成功完成宠漩,則事務(wù)中的所有操作都需要被回滾举反,回到事務(wù)執(zhí)行前的狀態(tài);同時(shí),該事務(wù)對(duì)數(shù)據(jù)庫(kù)或者其他事務(wù)的執(zhí)行無(wú)影響扒吁,所有的事務(wù)都好像在獨(dú)立的運(yùn)行。

事務(wù)的四大特性(ACID)

原子性(Atomicity):事務(wù)作為一個(gè)整體被執(zhí)行,包含在其中的對(duì)數(shù)據(jù)庫(kù)的操作要么全部被執(zhí)行雕崩,要么都不執(zhí)行魁索。
一致性(Consistency):事務(wù)應(yīng)確保數(shù)據(jù)庫(kù)的狀態(tài)從一個(gè)一致?tīng)顟B(tài)轉(zhuǎn)變?yōu)榱硪粋€(gè)一致?tīng)顟B(tài)。一致?tīng)顟B(tài)的含義是數(shù)據(jù)庫(kù)中的數(shù)據(jù)應(yīng)滿足完整性約束盼铁。
隔離性(Isolation):多個(gè)事務(wù)并發(fā)執(zhí)行時(shí)粗蔚,一個(gè)事務(wù)的執(zhí)行不應(yīng)影響其他事務(wù)的執(zhí)行。
持久性(Durability):已被提交的事務(wù)對(duì)數(shù)據(jù)庫(kù)的修改應(yīng)該永久保存在數(shù)據(jù)庫(kù)中饶火。

隔離性-隔離級(jí)別

并發(fā)的狀態(tài)下鹏控,事務(wù)會(huì)出現(xiàn)如下一些問(wèn)題:

  • 臟讀
    當(dāng)一個(gè)事務(wù)允許讀取另外一個(gè)事務(wù)修改但未提交的數(shù)據(jù)時(shí),就可能發(fā)生臟讀肤寝。
    當(dāng)一個(gè)事務(wù)正在訪問(wèn)數(shù)據(jù)并且對(duì)數(shù)據(jù)進(jìn)行了修改当辐,而這種修改還沒(méi)有提交到數(shù)據(jù)庫(kù)中,這時(shí)另外一個(gè)事務(wù)也訪問(wèn)這個(gè)數(shù)據(jù)鲤看,然后使用了這個(gè)數(shù)據(jù)缘揪。因?yàn)檫@個(gè)數(shù)據(jù)是還沒(méi)有提交的數(shù)據(jù),那么另外一個(gè)事務(wù)讀到的這個(gè)數(shù)據(jù)是臟數(shù)據(jù)义桂,依據(jù)臟數(shù)據(jù)所做的操作可能是不正確的找筝。

  • 不可重復(fù)讀
    一個(gè)事務(wù)內(nèi),多次讀同一個(gè)數(shù)據(jù)慷吊。在這個(gè)事務(wù)還沒(méi)有結(jié)束時(shí)袖裕,另一個(gè)事務(wù)也訪問(wèn)該同一數(shù)據(jù)。那么溉瓶,在第一個(gè)事務(wù)的兩次讀數(shù)據(jù)之間陆赋。由于第二個(gè)事務(wù)的修改,那么第一個(gè)事務(wù)讀到的數(shù)據(jù)可能不一樣嚷闭,這樣就發(fā)生了在一個(gè)事務(wù)內(nèi)兩次讀到的數(shù)據(jù)是不一樣的攒岛,因此稱(chēng)為不可重復(fù)讀,即原始讀取不可重復(fù)胞锰。

  • 幻影讀
    在事務(wù)執(zhí)行過(guò)程中灾锯,當(dāng)兩個(gè)完全相同的查詢(xún)語(yǔ)句執(zhí)行得到不同的結(jié)果集。這種現(xiàn)象稱(chēng)為“幻影讀(phantom read)”
    當(dāng)事務(wù)沒(méi)有獲取范圍鎖的情況下執(zhí)行SELECT ... WHERE操作可能會(huì)發(fā)生“幻影讀”嗅榕。
    “幻影讀”是不可重復(fù)讀的一種特殊場(chǎng)景:當(dāng)事務(wù)1兩次執(zhí)行SELECT ... WHERE檢索一定范圍內(nèi)數(shù)據(jù)的操作中間顺饮,事務(wù)2在這個(gè)表中創(chuàng)建了(如INSERT)了一行新數(shù)據(jù),這條新數(shù)據(jù)正好滿足事務(wù)1的“WHERE”子句凌那。
    需要指出的是事務(wù)1執(zhí)行了兩遍同樣的查詢(xún)語(yǔ)句兼雄,第二次查詢(xún)可能會(huì)得到不同的結(jié)果集。

隔離級(jí)別

為了解決上述問(wèn)題帽蝶,引入了不同的隔離級(jí)別來(lái)解決:

  • 未提交讀
    未提交讀(READ UNCOMMITTED)是最低的隔離級(jí)別赦肋。允許“臟讀”(dirty reads),事務(wù)可以看到其他事務(wù)“尚未提交”的修改。
  • 提交讀
    在提交讀(READ COMMITTED)級(jí)別中佃乘,基于鎖機(jī)制并發(fā)控制的DBMS需要對(duì)選定對(duì)象的寫(xiě)鎖一直保持到事務(wù)結(jié)束囱井,但是讀鎖在SELECT操作完成后馬上釋放(因此“不可重復(fù)讀”現(xiàn)象可能會(huì)發(fā)生,見(jiàn)下面描述)趣避。和前一種隔離級(jí)別一樣庞呕,也不要求“范圍鎖”。
  • 可重復(fù)讀
    在可重復(fù)讀(REPEATABLE READS)隔離級(jí)別中程帕,基于鎖機(jī)制并發(fā)控制的DBMS需要對(duì)選定對(duì)象的讀鎖(read locks)和寫(xiě)鎖(write locks)一直保持到事務(wù)結(jié)束住练,但不要求“范圍鎖”,因此可能會(huì)發(fā)生“幻影讀”愁拭。
  • 可串行化
    最高的隔離級(jí)別讲逛。在基于鎖機(jī)制并發(fā)控制的DBMS實(shí)現(xiàn)可串行化,要求在選定對(duì)象上的讀鎖和寫(xiě)鎖保持直到事務(wù)結(jié)束后才能釋放敛苇。在SELECT 的查詢(xún)中使用一個(gè)“WHERE”子句來(lái)描述一個(gè)范圍時(shí)應(yīng)該獲得一個(gè)“范圍鎖”(range-locks)妆绞。這種機(jī)制可以避免“幻影讀”(phantom reads)現(xiàn)象。當(dāng)采用不基于鎖的并發(fā)控制時(shí)不用獲取鎖枫攀。但當(dāng)系統(tǒng)探測(cè)到幾個(gè)并發(fā)事務(wù)有“寫(xiě)沖突”的時(shí)候括饶,只有其中一個(gè)是允許提交的。

隨著隔離級(jí)別的提高来涨,能夠避免的問(wèn)題也越多图焰,但同時(shí)帶來(lái)的開(kāi)銷(xiāo)也迅速增加:

隔離級(jí)別 臟讀 不可重復(fù)讀 幻影讀
未提交讀 可能發(fā)生 可能發(fā)生 可能發(fā)生
提交讀 - 可能發(fā)生 可能發(fā)生
可重復(fù)讀 - - 可能發(fā)生
可序列化 - - -

MySQL事務(wù)的原理

MySQL的事務(wù)分類(lèi)

  • 扁平事務(wù)
    扁平事務(wù)(Flat Transactions)是事務(wù)類(lèi)型中最簡(jiǎn)單但使用最頻繁的事務(wù)。在扁平事務(wù)中蹦掐,所有的操作都處于同一層次技羔,由BEGIN/START TRANSACTION開(kāi)始事務(wù),由COMMIT/ROLLBACK結(jié)束卧抗,且都是原子的藤滥,要么都執(zhí)行,要么都回滾社裆。因此扁平事務(wù)是應(yīng)用程序成為原子操作的基本組成模塊拙绊。

  • 帶有保存節(jié)點(diǎn)的扁平事務(wù)
    帶有保存節(jié)點(diǎn)的扁平事務(wù)(Flat Transactions with Savepoints)允許事務(wù)在執(zhí)行過(guò)程中回滾到較早的一個(gè)狀態(tài),而不是回滾所有的操作泳秀。保存點(diǎn)(Savepoint)用來(lái)通知系統(tǒng)應(yīng)該記住事務(wù)當(dāng)前的狀態(tài)标沪,以便當(dāng)之后發(fā)生錯(cuò)誤時(shí),事務(wù)能回到保存點(diǎn)當(dāng)時(shí)的狀態(tài)嗜傅。
    對(duì)于扁平事務(wù)來(lái)說(shuō)金句,在事務(wù)開(kāi)始時(shí)隱式地設(shè)置了一個(gè)保存點(diǎn),回滾時(shí)只能回滾到事務(wù)開(kāi)始時(shí)的狀態(tài)

  • 鏈?zhǔn)聞?wù)
    鏈?zhǔn)聞?wù)(Chained Transaction)是指一個(gè)事務(wù)由多個(gè)子事務(wù)鏈?zhǔn)浇M成吕嘀。前一個(gè)子事務(wù)的提交操作和下一個(gè)子事務(wù)的開(kāi)始操作合并成一個(gè)原子操作违寞,這意味著下一個(gè)事務(wù)將看到上一個(gè)事務(wù)的結(jié)果贞瞒,就好像在一個(gè)事務(wù)中進(jìn)行的一樣。這樣坞靶,在提交子事務(wù)時(shí)就可以釋放不需要的數(shù)據(jù)對(duì)象憔狞,而不必等到整個(gè)事務(wù)完成后才釋放蝴悉。
    鏈?zhǔn)聞?wù)與帶保存節(jié)點(diǎn)的扁平事務(wù)不同的是彰阴,鏈?zhǔn)聞?wù)中的回滾僅限于當(dāng)前事務(wù),相當(dāng)于只能恢復(fù)到最近的一個(gè)保存節(jié)點(diǎn)拍冠,而帶保存節(jié)點(diǎn)的扁平事務(wù)能回滾到任意正確的保存點(diǎn)尿这。但是,帶有保存節(jié)點(diǎn)的扁平事務(wù)中的保存點(diǎn)是易失的庆杜,當(dāng)發(fā)生系統(tǒng)崩潰是射众,所有的保存點(diǎn)都將消失,這意味著當(dāng)進(jìn)行恢復(fù)時(shí)晃财,事務(wù)需要從開(kāi)始處重新執(zhí)行叨橱。

  • 嵌套事務(wù)
    嵌套事務(wù)(Nested Transaction)是一個(gè)層次結(jié)構(gòu)框架。由一個(gè)頂層事務(wù)(top-level transaction)控制著各個(gè)層次的事務(wù)断盛。頂層事務(wù)之下嵌套的事務(wù)成為子事務(wù)(subtransaction),其控制著每一個(gè)局部的操作罗洗,子事務(wù)本身也可以是嵌套事務(wù)。
    MySQL本身是不支持嵌套事務(wù)的钢猛,每次start transaction 都會(huì)隱式提交伙菜。

  • 分布式事務(wù)
    分布式事務(wù)(Distributed Transactions)通常是一個(gè)在分布式環(huán)境下運(yùn)行的扁平事務(wù),因此需要根據(jù)數(shù)據(jù)所在位置訪問(wèn)網(wǎng)絡(luò)中不同節(jié)點(diǎn)的數(shù)據(jù)庫(kù)資源命迈,分布式環(huán)境下運(yùn)行的扁平事務(wù).
    牽涉到分布式贩绕,就默默埋下一個(gè)大坑好了,寫(xiě)起來(lái)也是能有一篇文章壶愤。

MySQL事務(wù)控制

BEGIN或START TRANSACTION淑倾;顯式地開(kāi)啟一個(gè)事務(wù);
COMMIT征椒;也可以使用COMMIT WORK娇哆,不過(guò)二者是等價(jià)的。COMMIT會(huì)提交事務(wù)陕靠,并使已對(duì)數(shù)據(jù)庫(kù)進(jìn)行的所有修改稱(chēng)為永久性的迂尝;
ROLLBACK;有可以使用ROLLBACK WORK剪芥,不過(guò)二者是等價(jià)的垄开。回滾會(huì)結(jié)束用戶(hù)的事務(wù)税肪,并撤銷(xiāo)正在進(jìn)行的所有未提交的修改溉躲;
SAVEPOINT identifier榜田;SAVEPOINT允許在事務(wù)中創(chuàng)建一個(gè)保存點(diǎn),一個(gè)事務(wù)中可以有多個(gè)SAVEPOINT锻梳;
RELEASE SAVEPOINT identifier箭券;刪除一個(gè)事務(wù)的保存點(diǎn),當(dāng)沒(méi)有指定的保存點(diǎn)時(shí)疑枯,執(zhí)行該語(yǔ)句會(huì)拋出一個(gè)異常辩块;
ROLLBACK TO identifier;把事務(wù)回滾到標(biāo)記點(diǎn)荆永;
SET TRANSACTION废亭;用來(lái)設(shè)置事務(wù)的隔離級(jí)別。InnoDB存儲(chǔ)引擎提供事務(wù)的隔離級(jí)別有READ UNCOMMITTED具钥、READ COMMITTED豆村、REPEATABLE READ和SERIALIZABLE。
MYSQL 事務(wù)處理主要有兩種方法:

  1. 用 BEGIN, ROLLBACK, COMMIT來(lái)實(shí)現(xiàn)
    BEGIN 開(kāi)始一個(gè)事務(wù)
    ROLLBACK 事務(wù)回滾
    COMMIT 事務(wù)確認(rèn)

  2. 直接用 SET 來(lái)改變 MySQL 的自動(dòng)提交模式:
    SET AUTOCOMMIT=0 禁止自動(dòng)提交
    SET AUTOCOMMIT=1 開(kāi)啟自動(dòng)提交

Tips

在 MySQL 命令行的默認(rèn)設(shè)置下骂删,事務(wù)都是自動(dòng)提交的掌动,即執(zhí)行 SQL 語(yǔ)句后就會(huì)馬上執(zhí)行 COMMIT 操作。因此要顯式地開(kāi)啟一個(gè)事務(wù)務(wù)須使用命令 BEGIN 或 START TRANSACTION宁玫,或者執(zhí)行命令 SET AUTOCOMMIT=0粗恢,用來(lái)禁止使用當(dāng)前會(huì)話的自動(dòng)提交。
在 MySQL 中只有使用了 Innodb 數(shù)據(jù)庫(kù)引擎的數(shù)據(jù)庫(kù)或表才支持事務(wù)撬统。
事務(wù)用來(lái)管理 insert, update, delete 語(yǔ)句
在MySQL數(shù)據(jù)庫(kù)中适滓,默認(rèn)的隔離級(jí)別為Repeatable read (可重復(fù)讀);


Spring的事務(wù)管理

OK, 到這叨叨了一大波原理恋追,Talk is cheap凭迹,Show me the code。下面用代碼來(lái)說(shuō)下Spring對(duì)事務(wù)是如何進(jìn)行管理的苦囱。
Spring通過(guò)AOP嗅绸,抽象統(tǒng)一的接口來(lái)進(jìn)行聲明式事務(wù)管理,好處是它將事務(wù)管理代碼從業(yè)務(wù)方法中分離出來(lái)撕彤,并且讓開(kāi)發(fā)按人員可以不用關(guān)注具體事務(wù)管理方式的實(shí)現(xiàn)鱼鸠;
可采用xml配置或者注解來(lái)實(shí)現(xiàn)。

以下是基于Spring 4.X 的通用事務(wù)管理配置文件:
此處采用通用bean配置羹铅,其他配置方式蚀狰,可以參照
Spring事務(wù)配置的五種方式和spring里面事務(wù)的傳播屬性和事務(wù)隔離級(jí)別

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<!-- 
常見(jiàn)的TranszctionManager類(lèi)
org.springframework.orm.jpa.JpaTransactionManager   使用JPA持久化使用該事務(wù)管理器
org.springframework.orm.hibernate3.HibernateTransactionManager  Hibernate3.o事務(wù)管理器
org.springframework.jdbc.datasource.DataSourceTransactionManager SpringJdbc或ibatis等基于DataSource數(shù)據(jù)源持久化技術(shù)時(shí)使用管理器
org.springframework.orm.jdo.JdoTransactionManager   使用JDO持久化使用該事務(wù)管理器
org.springframework.transaction.jta.JtaTranszctionManager   具有多個(gè)數(shù)據(jù)源的全局事務(wù)使用事務(wù)管理器

目前普遍使用DataSourceTransactionManager居多,配合MyBatis
-->
  <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource" />
  </bean>
  <bean id="txTransactionInterceptor"class="org.springframework.transaction.interceptor.TransactionInterceptor">
    <property name="transactionManager" ref="txManager" />
    <property name="transactionAttributes">
<!--
用正則匹配掃描方法职员,以save麻蹋,update,delete開(kāi)頭的方法焊切,使用事務(wù)管理

Spring在TransactionDefinition接口中規(guī)定了7中類(lèi)型的事務(wù)傳播行為扮授,規(guī)定了事務(wù)方法和事務(wù)方法發(fā)生嵌套調(diào)用時(shí)事務(wù)如何傳播的芳室。
propagation_required:如果當(dāng)前沒(méi)有事務(wù),就新建一個(gè)事務(wù)刹勃,如果已經(jīng)存在堪侯,就加入到存在的事務(wù)中,這是最常見(jiàn)的選擇
propagation_supports:支持當(dāng)前的事務(wù)荔仁,如果當(dāng)前沒(méi)有事務(wù)伍宦,就按非事務(wù)模式執(zhí)行
propagation_mandatory:使用當(dāng)前事務(wù),如果當(dāng)前沒(méi)有事務(wù)咕晋,拋出異常
propagation_requires_new:新建事務(wù)雹拄,如果當(dāng)前存在事務(wù)收奔,把當(dāng)前事務(wù)掛起
propagation_not_supported:以非事務(wù)方式執(zhí)行掌呜,如果當(dāng)前存在事務(wù),就把當(dāng)前事務(wù)掛起
propagation_never:以非事務(wù)方式執(zhí)行坪哄,如果當(dāng)前存在事務(wù)质蕉,則拋出異常
propagation_nested:如果當(dāng)前存在事務(wù),則在嵌套事務(wù)內(nèi)執(zhí)行翩肌,如果沒(méi)有事務(wù)模暗,則執(zhí)行與propagation_required類(lèi)似的操作

回滾策略:
在碰到哪些異常的時(shí)候回滾,
支持Exception念祭,CheckedException兑宇,以及自定義異常
- 表示拋出該異常時(shí)需要回滾
+表示即使拋出該異常事務(wù)同樣要提交
-->
      <props>
        <prop key="save*">PROPAGATION_REQUIRED,-Exception</prop>
        <prop key="update*">PROPAGATION_REQUIRED,-Exception</prop>
        <prop key="delete*">PROPAGATION_REQUIRED,-Exception</prop>
      </props>
    </property>
  </bean>

<!--AOP切片,在配置的包中進(jìn)行掃描匹配的方法進(jìn)行事務(wù)管理-->
  <bean id="txTransactionPointcutAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
    <property name="advice">
      <ref bean="txTransactionInterceptor"/>
    </property>
    <property name="patterns">
      <list>
        <value>com.test.service.impl.*.*</value>
      </list>
    </property>
  </bean>

  <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
    <property name="proxyTargetClass" value="true"></property>
    <property name="beanNames">
      <value>*ServiceImpl</value>
    </property>
    <property name="interceptorNames">
       <list>
        <value>txTransactionPointcutAdvisor</value>
      </list>
    </property>
  </bean>
</beans>

以下是基于Spring 4.X 的注解式實(shí)現(xiàn)的例子:

//添加事務(wù)注解
//1.使用 propagation 指定事務(wù)的傳播行為, 即當(dāng)前的事務(wù)方法被另外一個(gè)事務(wù)方法調(diào)用時(shí)如何使用事務(wù), 取值見(jiàn)上XML配置注解
//2.使用 isolation 指定事務(wù)的隔離級(jí)別, 最常用的取值為 READ_COMMITTED粱坤,參見(jiàn)隔離級(jí)別隶糕,支持DEFAULT,READ_UNCOMMITTED站玄,READ_COMMITTED枚驻,REPEATABLE_READ,SERIALIZABLE
//3.默認(rèn)情況下 Spring 的聲明式事務(wù)對(duì)所有的運(yùn)行時(shí)異常進(jìn)行回滾株旷,也可以通過(guò)對(duì)應(yīng)的屬性進(jìn)行設(shè)置再登,通常情況下去默認(rèn)值即可,取值見(jiàn)上XML配置注解
//no-rollback-for 對(duì)應(yīng)xml中的+Exception
//4.使用 readOnly 指定事務(wù)是否為只讀. 表示這個(gè)事務(wù)只讀取數(shù)據(jù)但不更新數(shù)據(jù),這樣可以幫助數(shù)據(jù)庫(kù)引擎優(yōu)化事務(wù). 若真的事一個(gè)只讀取數(shù)據(jù)庫(kù)值的方法, 應(yīng)設(shè)置 readOnly=true,或者直接不使用事務(wù)
//5.使用 timeout 指定強(qiáng)制回滾之前事務(wù)可以占用的時(shí)間
@Transactional(propagation=Propagation.REQUIRES_NEW,
            isolation=Isolation.READ_COMMITTED,
            rollbackFor = { Exception.class },
            //noRollbackFor = {Exception.class},
            readOnly=false,
            timeout=3)
public void crud(){
  try {  
    doCURD();
    //這里為了說(shuō)明晾剖,拋異常時(shí)锉矢,如果沒(méi)有使用try catch ,則spring會(huì)回滾齿尽,doCURD中對(duì)數(shù)據(jù)庫(kù)的操作都不會(huì)執(zhí)行
    throw new Exception();
   } catch (Exception e) {  
      e.printStackTrace(); 
      //如果用了try catch沽损,在catch里面再拋出一個(gè)指定異常,這樣出了異常才會(huì)回滾
      //但是加上這句之后拋了異常就能回滾(有這句代碼就不需要再手動(dòng)拋出運(yùn)行時(shí)異常了)
      TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();  
     } 
}

遇到的坑

  1. 嵌套事務(wù)的傳遞
    當(dāng)Service A 調(diào)用Service B時(shí)雕什,就會(huì)產(chǎn)生邏輯上的嵌套事務(wù)缠俺,Spring 的處理如下:
    PROPAGATION_REQUIRED 如果當(dāng)前線程中已經(jīng)存在事務(wù), 方法調(diào)用會(huì)加入此事務(wù), 如果當(dāng)前沒(méi)有事務(wù)显晶,就新建一個(gè)事務(wù)

PROPAGATION_REQUIRES_NEW 啟動(dòng)一個(gè)新的, 不依賴(lài)于環(huán)境的 "內(nèi)部" 事務(wù). 這個(gè)事務(wù)將被完全 commited 或 rolled back 而不依賴(lài)于外部事務(wù), 它擁有自己的隔離范圍, 自己的鎖, 等等. 當(dāng)內(nèi)部事務(wù)開(kāi)始執(zhí)行時(shí), 外部事務(wù)將被掛起, 內(nèi)務(wù)事務(wù)結(jié)束時(shí), 外部事務(wù)將繼續(xù)執(zhí)行。

另一方面, PROPAGATION_NESTED 開(kāi)始一個(gè) "嵌套的" 事務(wù), 它是已經(jīng)存在事務(wù)的一個(gè)真正的子事務(wù). 嵌套事務(wù)開(kāi)始執(zhí)行時(shí), 它將取得一個(gè) savepoint. 如果這個(gè)嵌套事務(wù)失敗, 我們將回滾到此 savepoint. 嵌套事務(wù)是外部事務(wù)的一部分, 只有外部事務(wù)結(jié)束后它才會(huì)被提交壹士。

由此PROPAGATION_REQUIRES_NEW 和 PROPAGATION_NESTED 的最大區(qū)別在于, PROPAGATION_REQUIRES_NEW 完全是一個(gè)新的事務(wù), 而 PROPAGATION_NESTED 則是外部事務(wù)的子事務(wù), 如果外部事務(wù) commit, 嵌套事務(wù)也會(huì)被 commit, 這個(gè)規(guī)則同樣適用于 roll back磷雇。

當(dāng)數(shù)據(jù)庫(kù)處理結(jié)果與預(yù)期不符,可以檢查配置是否合理躏救。

  1. 異常的回滾
    在使用try catch塊時(shí)需要注意回滾策略唯笙,詳見(jiàn)上java代碼

  2. MySQL的引擎
    當(dāng)MySQL的引擎配置為MyISAM(默認(rèn))時(shí),是不支持事務(wù)的盒使,Spring配置得再6崩掘,也不會(huì)起作用,目前普遍使用InnoDB少办,可以參考
    MyISAM與InnoDB兩者之間區(qū)別與選擇苞慢,詳細(xì)總結(jié),性能對(duì)比

  3. 使用Spring MVC時(shí)英妓,加載順序不當(dāng)導(dǎo)致事務(wù)無(wú)效
    spring的容器(applicationContext)和springMVC的(applicationContext)是不同的挽放。
    spring容器加載得時(shí)候,優(yōu)先加載ServletContextListener(對(duì)應(yīng)spring.xml)產(chǎn)生的父容器蔓纠,而springMVC(對(duì)應(yīng)springMVC.xml)產(chǎn)生的是子容器辑畦。子容器Controller進(jìn)行掃描裝配時(shí)裝配的@Service注解的實(shí)例是沒(méi)有經(jīng)過(guò)事務(wù)加強(qiáng)處理,
    即沒(méi)有事務(wù)處理能力的Service腿倚。而父容器進(jìn)行初始化的Service是保證事務(wù)的增強(qiáng)處理能力的纯出。如果不在子容器中將Service除去掉,此時(shí)得到的將是原樣的無(wú)事務(wù)處理能力的Service敷燎。
    所以暂筝,我們應(yīng)把掃描Service的工作放在spring.xml中。讓Service和事務(wù)注解存在于同一個(gè)容器中懈叹,這樣配置的事務(wù)注解就能起作用了乖杠。


總結(jié)

總結(jié)一下:

  • 本文闡述了事務(wù)的基本概念:是由一個(gè)有限的數(shù)據(jù)庫(kù)操作序列構(gòu)成在系統(tǒng)執(zhí)行過(guò)程中的一個(gè)邏輯單位
  • 事務(wù)的四大特性(ACID):原子性(A),一致性(C)澄成,隔離性(I)胧洒,持久性(Durability)
  • 事務(wù)的隔離級(jí)別:未提交讀(READ UNCOMMITTED),在提交讀(READ COMMITTED)墨状,在可重復(fù)讀(REPEATABLE READS)卫漫,可串行化(Serializable)
  • 隔離級(jí)別解決對(duì)應(yīng)的問(wèn)題:臟讀,不可重復(fù)讀肾砂,幻影讀
  • MySQL的事務(wù)類(lèi)型:扁平事務(wù)列赎,帶有保存節(jié)點(diǎn)的扁平事務(wù),鏈?zhǔn)聞?wù)镐确,嵌套事務(wù)包吝,分布式事務(wù)
  • MySQL的事務(wù)控制:BEGIN, ROLLBACK, COMMIT以及SET AUTOCOMMIT兩種方式
  • Spring對(duì)事務(wù)的管理饼煞,以及常見(jiàn)的一些問(wèn)題

謝謝閱讀,希望能對(duì)你有所幫助诗越。


思考題

留個(gè)思考題給大家砖瞧,思考一下數(shù)據(jù)庫(kù)的事務(wù)隔離機(jī)制的實(shí)現(xiàn)和多線程中的并發(fā)控制有何異同,我們有緣再聊聊這個(gè)問(wèn)題


引用:

維基百科:數(shù)據(jù)庫(kù)事務(wù)

維基百科:事務(wù)隔離

菜鳥(niǎo)教程:MySQL事務(wù)

以及其他一些博文的零碎引用嚷狞,深表感謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末块促,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子床未,更是在濱河造成了極大的恐慌竭翠,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件薇搁,死亡現(xiàn)場(chǎng)離奇詭異斋扰,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)只酥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)褥实,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人裂允,你說(shuō)我怎么就攤上這事「缤В” “怎么了绝编?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)貌踏。 經(jīng)常有香客問(wèn)我十饥,道長(zhǎng),這世上最難降的妖魔是什么祖乳? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任逗堵,我火速辦了婚禮,結(jié)果婚禮上眷昆,老公的妹妹穿的比我還像新娘蜒秤。我一直安慰自己,他們只是感情好亚斋,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布作媚。 她就那樣靜靜地躺著,像睡著了一般帅刊。 火紅的嫁衣襯著肌膚如雪纸泡。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,262評(píng)論 1 308
  • 那天赖瞒,我揣著相機(jī)與錄音女揭,去河邊找鬼蚤假。 笑死,一個(gè)胖子當(dāng)著我的面吹牛吧兔,可吹牛的內(nèi)容都是我干的勤哗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼掩驱,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼芒划!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起欧穴,我...
    開(kāi)封第一講書(shū)人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤民逼,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后涮帘,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體拼苍,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年调缨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疮鲫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡弦叶,死狀恐怖俊犯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伤哺,我是刑警寧澤燕侠,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站立莉,受9級(jí)特大地震影響绢彤,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜蜓耻,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一茫舶、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧刹淌,春花似錦饶氏、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至柠衅,卻和暖如春皮仁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工贷祈, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留趋急,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓势誊,卻偏偏與公主長(zhǎng)得像呜达,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子粟耻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容