了解事務(wù)之前得了解幾個(gè)概念:
1.臟讀:
臟讀簡(jiǎn)單來(lái)說(shuō)就是提取未提交的數(shù)據(jù)
A事務(wù)讀取B事務(wù)尚未提交的數(shù)據(jù),此時(shí)如果B事務(wù)發(fā)生錯(cuò)誤并執(zhí)行回滾操作书妻,那么A事務(wù)讀取到的數(shù)據(jù)就是臟數(shù)據(jù)船响。就好像原本的數(shù)據(jù)比較干凈、純粹躲履,此時(shí)由于B事務(wù)更改了它见间,這個(gè)數(shù)據(jù)變得不再純粹。這個(gè)時(shí)候A事務(wù)立即讀取了這個(gè)臟數(shù)據(jù)工猜,但事務(wù)B良心發(fā)現(xiàn)米诉,又用回滾把數(shù)據(jù)恢復(fù)成原來(lái)干凈、純粹的樣子篷帅,而事務(wù)A卻什么都不知道史侣,最終結(jié)果就是事務(wù)A讀取了此次的臟數(shù)據(jù),稱為臟讀魏身。
這種情況常發(fā)生于轉(zhuǎn)賬與取款操作中
2.不可重復(fù)讀
簡(jiǎn)單來(lái)說(shuō)就是前后多次讀取惊橱,數(shù)據(jù)內(nèi)容不一致
事務(wù)A在執(zhí)行讀取操作,由整個(gè)事務(wù)A比較大箭昵,前后讀取同一條數(shù)據(jù)需要經(jīng)歷很長(zhǎng)的時(shí)間 税朴。而在事務(wù)A第一次讀取數(shù)據(jù),比如此時(shí)讀取了小明的年齡為20歲家制,事務(wù)B執(zhí)行更改操作正林,將小明的年齡更改為30歲,此時(shí)事務(wù)A第二次讀取到小明的年齡時(shí)颤殴,發(fā)現(xiàn)其年齡是30歲觅廓,和之前的數(shù)據(jù)不一樣了,也就是數(shù)據(jù)不重復(fù)了诅病,系統(tǒng)不可以讀取到重復(fù)的數(shù)據(jù)哪亿,成為不可重復(fù)讀粥烁。
3.幻讀
事務(wù)A在執(zhí)行讀取操作,需要兩次統(tǒng)計(jì)數(shù)據(jù)的總量蝇棉,前一次查詢數(shù)據(jù)總量后讨阻,此時(shí)事務(wù)B執(zhí)行了新增數(shù)據(jù)的操作并提交后,這個(gè)時(shí)候事務(wù)A讀取的數(shù)據(jù)總量和之前統(tǒng)計(jì)的不一樣篡殷,就像產(chǎn)生了幻覺(jué)一樣钝吮,平白無(wú)故的多了幾條數(shù)據(jù),成為幻讀板辽。
數(shù)據(jù)庫(kù)事務(wù)
事務(wù)的基本要素(ACID)
1奇瘦、原子性(Atomicity):事務(wù)開(kāi)始后所有操作,要么全部做完劲弦,要么全部不做耳标,不可能停滯在中間環(huán)節(jié)。事務(wù)執(zhí)行過(guò)程中出錯(cuò)邑跪,會(huì)回滾到事務(wù)開(kāi)始前的狀態(tài)次坡,所有的操作就像沒(méi)有發(fā)生一樣。也就是說(shuō)事務(wù)是一個(gè)不可分割的整體画畅,就像化學(xué)中學(xué)過(guò)的原子砸琅,是物質(zhì)構(gòu)成的基本單位。
?2轴踱、一致性(Consistency):事務(wù)開(kāi)始前和結(jié)束后症脂,數(shù)據(jù)庫(kù)的完整性約束沒(méi)有被破壞 。比如A向B轉(zhuǎn)賬淫僻,不可能A扣了錢诱篷,B卻沒(méi)收到。
3嘁傀、隔離性(Isolation):同一時(shí)間兴蒸,只允許一個(gè)事務(wù)請(qǐng)求同一數(shù)據(jù),不同的事務(wù)之間彼此沒(méi)有任何干擾细办。比如A正在從一張銀行卡中取錢,在A取錢的過(guò)程結(jié)束前蕾殴,B不能向這張卡轉(zhuǎn)賬笑撞。
4、持久性(Durability):事務(wù)完成后钓觉,事務(wù)對(duì)數(shù)據(jù)庫(kù)的所有更新將被保存到數(shù)據(jù)庫(kù)茴肥,不能回滾。
事務(wù)隔離
數(shù)據(jù)庫(kù)有四種隔離級(jí)別:
讀未提交(Read uncommitted)
讀已提交(Read committed)
可重復(fù)讀(Repeatable read)
可串行化(Serializable)
讀未提交
在這種隔離級(jí)別下荡灾,所有事務(wù)能夠讀取其他事務(wù)未提交的數(shù)據(jù)瓤狐。讀取其他事務(wù)未提交的數(shù)據(jù)瞬铸,會(huì)造成臟讀。因此在該種隔離級(jí)別下础锐,不能解決臟讀嗓节、不可重復(fù)讀和幻讀。
讀已提交
在這種隔離級(jí)別下皆警,所有事務(wù)只能讀取其他事務(wù)已經(jīng)提交的內(nèi)容拦宣。能夠徹底解決臟讀的現(xiàn)象。但在這種隔離級(jí)別下信姓,會(huì)出現(xiàn)一個(gè)事務(wù)的前后多次的查詢中卻返回了不同內(nèi)容的數(shù)據(jù)的現(xiàn)象鸵隧,也就是出現(xiàn)了不可重復(fù)讀。這是大多數(shù)數(shù)據(jù)庫(kù)系統(tǒng)默認(rèn)的隔離級(jí)別意推,例如Oracle和SQL Server豆瘫,但mysql不是。
可重復(fù)讀
在這種隔離級(jí)別下菊值,所有事務(wù)前后多次的讀取到的數(shù)據(jù)內(nèi)容是不變的靡羡。也就是某個(gè)事務(wù)在執(zhí)行的過(guò)程中,不允許其他事務(wù)進(jìn)行update操作俊性,但允許其他事務(wù)進(jìn)行add操作略步,造成某個(gè)事務(wù)前后多次讀取到的數(shù)據(jù)總量不一致的現(xiàn)象,從而產(chǎn)生幻讀定页。這是mysql的默認(rèn)事務(wù)隔離級(jí)別.
可串行化
在這種隔離級(jí)別下趟薄,所有的事務(wù)順序執(zhí)行,所以他們之間不存在沖突典徊,從而能有效地解決臟讀杭煎、不可重復(fù)讀和幻讀的現(xiàn)象。但是安全和效率不能兼得卒落,這樣事務(wù)隔離級(jí)別羡铲,會(huì)導(dǎo)致大量的操作超時(shí)和鎖競(jìng)爭(zhēng),從而大大降低數(shù)據(jù)庫(kù)的性能儡毕,一般不使用這樣事務(wù)隔離級(jí)別也切。
SpringBoot中使用事務(wù)
在SpringBoot中只要導(dǎo)入依賴就可以使用事務(wù)注解@Transactional注解完成事務(wù)。
導(dǎo)入依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
@Transactional
public String getTest(){
return testDao.getTest();
}
事務(wù)的傳播屬性(propagation):
假設(shè)有ServieA和ServiceB
ServiceA?{???????????
void?methodA()?{??
?????????ServiceB.methodB();??
?????}??
}??????
ServiceB?{???????????
void?methodB()?{??
?????}???????????
} ?
1.@Transactional(propagation = Propagation.REQUIRED) //默認(rèn)使用REQUIRED
假設(shè)ServiceB.methodB的事務(wù)級(jí)別定義為REQUIRED
假如methodA已經(jīng)起了一個(gè)事務(wù)腰湾,那么在運(yùn)行methodB的時(shí)候B不會(huì)另起一個(gè)事務(wù)雷恃,這時(shí)只有外部事務(wù)并且他們是共用的,所以這時(shí)ServiceA.methodA或者ServiceB.methodB無(wú)論哪個(gè)發(fā)生異常methodA和methodB作為一個(gè)整體都將一起回滾费坊。
如果ServiceA.methodA沒(méi)有事務(wù)倒槐,ServiceB.methodB就會(huì)為自己分配一個(gè)事務(wù)。這樣附井,在ServiceA.methodA中是沒(méi)有事務(wù)控制的讨越。只是在ServiceB.methodB內(nèi)的任何地方出現(xiàn)異常两残,ServiceB.methodB將會(huì)被回滾,不會(huì)引起ServiceA.methodA的回滾
2.@Transactional(propagation = Propagation.SUPPORTS)
假設(shè)ServiceB.methodB的事務(wù)級(jí)別定義為Supports
如果其他bean調(diào)用這個(gè)方法,在其他bean中聲明事務(wù),那就用事務(wù).如果其他bean沒(méi)有聲明事務(wù),那就不用事務(wù).
如methodA有事務(wù)把跨,那調(diào)用methodB時(shí)人弓,methodB也有事務(wù),如果methodA沒(méi)有事務(wù)节猿,那調(diào)用methodB時(shí)票从,methodB也沒(méi)事務(wù)
3.@Transactional(propagation = Propagation.NOT_SUPPORTED)
以非事務(wù)方式執(zhí)行操作,如果當(dāng)前存在事務(wù)滨嘱,就把當(dāng)前事務(wù)掛起峰鄙。?
4.@Transactional(propagation = Propagation.MANDATORY)
使用當(dāng)前的事務(wù),如果當(dāng)前沒(méi)有事務(wù)太雨,就拋出異常吟榴。
假設(shè)ServiceB.methodB的事務(wù)級(jí)別定義為MANDATORY
如果methodA沒(méi)有設(shè)置事務(wù),則調(diào)用methodB時(shí)就會(huì)拋異常囊扳。
5.@Transactional(propagation = Propagation.REQUIRES_NEW)
新建事務(wù)吩翻,如果當(dāng)前存在事務(wù),把當(dāng)前事務(wù)掛起锥咸。
假設(shè)ServiceB.methodB的事務(wù)級(jí)別定義未REQUIRES_NEW狭瞎,
如果methodA沒(méi)有事務(wù),則methodA調(diào)用methodB時(shí)搏予,methodB會(huì)新建一個(gè)事務(wù)熊锭。
如果methodA有事務(wù),那么methodA調(diào)用methodB時(shí)雪侥,methodA的事務(wù)會(huì)暫時(shí)掛起碗殷,methodB新建一個(gè)事務(wù),若methodB拋異常速缨,A和B的事務(wù)都會(huì)回滾锌妻,若methodB提交后methodA拋異常,只有A事務(wù)會(huì)回滾旬牲。
6.@Transactional(propagation = Propagation.NEVER)
以非事務(wù)方式執(zhí)行仿粹,如果當(dāng)前存在事務(wù),則拋出異常引谜。
7.@Transactional(propagation = Propagation.NESTED)
支持當(dāng)前事務(wù)牍陌,如果當(dāng)前事務(wù)存在,則執(zhí)行一個(gè)嵌套事務(wù)员咽,如果當(dāng)前沒(méi)有事務(wù),就新建一個(gè)事務(wù)贮预。
事務(wù)隔離級(jí)別
@Transactional(isolation = Isolation.DEFAULT)
使用數(shù)據(jù)庫(kù)默認(rèn)的級(jí)別贝室,如mysql默認(rèn)使用Isolation.REPEATABLE_READ(可重復(fù)讀)
@Transactional(isolation = Isolation.READ_UNCOMMITTED)
未提交讀契讲,可能產(chǎn)生幻讀、不可重復(fù)讀滑频、臟讀
@Transactional(isolation = Isolation.READ_COMMITTED)
可能產(chǎn)生不可重復(fù)讀捡偏、幻讀
@Transactional(isolation = Isolation.REPEATABLE_READ)
可能產(chǎn)生幻讀
@Transactional(isolation = Isolation.SERIALIZABLE)
使用可串形化讀