一蠢琳、問(wèn)題描述
同事反饋一個(gè)問(wèn)題:一個(gè)spring事務(wù)方法A調(diào)用另外一個(gè)事務(wù)方法B(新增一條數(shù)據(jù)),該事務(wù)方法B返回新增數(shù)據(jù)的主鍵ID,該方法使用了注解@Transactional,傳播特性配置的是REQUIRES_NEW,很明顯他的意思是想獨(dú)立運(yùn)行一個(gè)事務(wù)執(zhí)行該方法归粉。但是奇怪的是方法A內(nèi)根據(jù)方法B返回的主鍵ID卻查詢不出新增的數(shù)據(jù)俩垃。
二、偽代碼
@Transactional
@Service
public class OtherService{
public void transactionA(){
//新事務(wù)
int id = transactionService.transactionB();//插入數(shù)據(jù)成功并且返回主鍵id
Object object = findById(id);//返回null
}
}
@Transactional
@Service
class TransactionService{
@Transactional(propagation = Propagation.REQUIRES_NEW)
public int transactionB(){
//省略
}
}
三畔塔、解決思路
看到問(wèn)題時(shí),我腦袋里冒出幾個(gè)想法:
- 會(huì)不會(huì)是事務(wù)傳播特性的問(wèn)題?
- 會(huì)不會(huì)是mysql隔離級(jí)別的問(wèn)題魏铅?
- 會(huì)不會(huì)和spring的嵌套事務(wù)有關(guān)?(本人并不是很清楚)
????????所以我只能根據(jù)我所了解的逐個(gè)排查监婶。由于方法B使用了REQUIRES_NEW傳播特性,所以當(dāng)方法B跳出方法棧時(shí),事務(wù)就已經(jīng)提交了。事實(shí)也是如此,打斷點(diǎn)發(fā)現(xiàn)數(shù)據(jù)庫(kù)中已新增了一條數(shù)據(jù)齿桃。所以我直接創(chuàng)建一個(gè)線程來(lái)執(zhí)行查詢操作压储,發(fā)現(xiàn)卻可以查到,因?yàn)槭聞?wù)方法A不會(huì)將事務(wù)傳播至新創(chuàng)建的線程源譬,一切都理所當(dāng)然集惋。所以更加懷疑是嵌套事務(wù)或者隔離級(jí)別在作怪。
????????真的會(huì)不會(huì)和嵌套事務(wù)有關(guān)呢踩娘?首先這個(gè)想法一直充斥著我的大腦,事務(wù)A中使用了事務(wù)B,本人又是個(gè)理想主義,覺(jué)得"好像"是嵌套了,又因?yàn)槭聞?wù)A沒(méi)有結(jié)束,所以不能查詢出事務(wù)B提交的新增數(shù)據(jù)刮刑。由于本人對(duì)嵌套事務(wù)理解不深,這里就不展開(kāi)了,下次重點(diǎn)學(xué)習(xí)下這個(gè)功能點(diǎn)。
????????實(shí)在沒(méi)辦法了,又開(kāi)始懷疑是不是mysql的隔離級(jí)別在作怪养渴。我早知道,我們的生產(chǎn)雷绢、測(cè)試數(shù)據(jù)庫(kù)隔離級(jí)別都是配置的repeatable read±肀埃可重復(fù)讀意味著其他事務(wù)新增的數(shù)據(jù)看不到,不正好就是這個(gè)現(xiàn)象么翘紊?為了證明下自己的觀點(diǎn),把mysql的隔離級(jí)別改成read committed,然后在試一次,發(fā)現(xiàn)確實(shí)可以查到數(shù)據(jù)了。哎~藐唠!最終解決辦法就是另外起一個(gè)事務(wù)查詢就沒(méi)問(wèn)題了帆疟。。宇立。
記錄下自己用的幾個(gè)mysql語(yǔ)句并且總結(jié)一下隔離級(jí)別的知識(shí):
--mysql查看隔離級(jí)別
select @@global.tx_isolation,@@tx_isolation;select @@global.tx_isolation,@@tx_isolation;
--mysql 設(shè)置隔離級(jí)別:
set global transaction isolation level read uncommitted;
隔離級(jí)別分類(lèi):
read uncommitted
可以看到未提交的數(shù)據(jù)(臟讀)踪宠,舉個(gè)例子:別人說(shuō)的話你都相信了,但是可能他只是說(shuō)說(shuō)妈嘹,并不實(shí)際做柳琢。read committed
讀取提交的數(shù)據(jù)。但是,可能多次讀取的數(shù)據(jù)結(jié)果不一致(不可重復(fù)讀柬脸,幻讀)他去。用讀寫(xiě)的觀點(diǎn)就是:讀取的行數(shù)據(jù),可以寫(xiě)倒堕。repeatable read(MySQL默認(rèn)隔離級(jí)別)
可以重復(fù)讀取灾测,但有幻讀。讀寫(xiě)觀點(diǎn):讀取的數(shù)據(jù)行不可寫(xiě)涩馆,但是可以往表中新增數(shù)據(jù)行施。在MySQL中允坚,其他事務(wù)新增的數(shù)據(jù)魂那,看不到,不會(huì)產(chǎn)生幻讀稠项。采用多版本并發(fā)控制(MVCC)機(jī)制解決幻讀問(wèn)題涯雅。serializable
可讀,不可寫(xiě)展运。像java中的鎖活逆,寫(xiě)數(shù)據(jù)必須等待另一個(gè)事務(wù)結(jié)束。
重點(diǎn):
????????重復(fù)讀與不可重復(fù)讀關(guān)注的在于某條數(shù)據(jù)拗胜。而幻讀與否關(guān)注的是整個(gè)表之內(nèi)的增刪操作蔗候。
????????可重復(fù)讀指的是在A事務(wù)進(jìn)行的過(guò)程中進(jìn)行了一次讀操作,這時(shí)B事務(wù)對(duì)此數(shù)據(jù)進(jìn)行了Update修改埂软,并commit锈遥,這時(shí)事務(wù)A再一次讀取此條數(shù)據(jù),讀取到的將不是B事務(wù)修改后的值勘畔,而是原值所灸。
????????幻讀指的是,在A事務(wù)涉及整個(gè)表的信息時(shí)炫七,B事務(wù)對(duì)表進(jìn)行了更改爬立,比如Insert,Delete万哪,并提交侠驯,這時(shí)A事務(wù)的操作將會(huì)受到影響。比如奕巍,公司的管理系統(tǒng)正在一個(gè)事務(wù)內(nèi)基于員工信息表統(tǒng)計(jì)員工數(shù)量陵霉,統(tǒng)計(jì)到一共30人,這時(shí)另一個(gè)事務(wù)要辦理員工入職手續(xù)伍绳,新增了一條記錄踊挠,并commit,這時(shí)第一個(gè)事務(wù)再進(jìn)行人數(shù)統(tǒng)計(jì),就會(huì)變成了31人效床。這樣睹酌,一個(gè)事務(wù)內(nèi)進(jìn)行兩次相同操作居然得到了不一樣的結(jié)果,這就是幻讀剩檀。
感謝~