最近優(yōu)化一個(gè)接口的性能,發(fā)現(xiàn)是為了保證數(shù)據(jù)一致性闲孤,需要依賴數(shù)據(jù)庫事務(wù)谆级。所以MySQL成為了性能的瓶頸。
業(yè)務(wù)需求:
扣除用戶的積分讼积,兌換庫存禮品肥照。
1、禮品不能超發(fā)勤众。
2舆绎、如禮品發(fā)放成功,必須成功扣減用戶的積分们颜,保證經(jīng)濟(jì)利益不受損害吕朵。
3、用戶禮品對換有次數(shù)限制掌桩。
代碼示例:
@啟動(dòng)事務(wù)
foo1(){
查詢禮品信息和數(shù)量()
判斷禮品是否存在边锁,return
查詢用戶的積分()
判斷用戶積分是否夠兌換禮品,return
foo2(禮品價(jià)值波岛,兌換數(shù)量)
}
foo2(禮品價(jià)值茅坛,兌換數(shù)量) {
統(tǒng)計(jì)用戶禮品兌換次數(shù)()
判斷兌換數(shù)量,return
用戶積分更新(禮品價(jià)值*兌換數(shù)量)
禮品數(shù)量更新(兌換數(shù)量)
}
從代碼中看,foo1被包含在整個(gè)數(shù)據(jù)庫事務(wù)中贡蓖。一個(gè)方法執(zhí)行曹鸠,所以的SQL查詢都被阻塞。
在這5條SQL中斥铺,foo2中的兩次更新操作必須在一起執(zhí)行彻桃。foo1中的查詢SQL可以不包含在數(shù)據(jù)庫事務(wù)中。查詢結(jié)果晾蜘,作為更新時(shí)的判斷條件邻眷,就可以保證查詢的數(shù)據(jù)在更新之前沒有被別人操作過。統(tǒng)計(jì)語句的結(jié)果剔交,需要在程序中進(jìn)行邏輯判斷肆饶。在更新時(shí),無法對統(tǒng)計(jì)結(jié)果是否改變進(jìn)行有效的判斷岖常,除非再次統(tǒng)計(jì)數(shù)據(jù)驯镊。
通過上面的分析對代碼進(jìn)行了適當(dāng)?shù)母脑欤?/p>
foo1(){
查詢禮品信息和數(shù)量()
判斷禮品是否存在,return
查詢用戶的積分()
判斷用戶積分是否夠兌換禮品竭鞍,return
foo2(禮品價(jià)值板惑,兌換數(shù)量,用戶當(dāng)前積分)
}
@啟動(dòng)事務(wù)
foo2(禮品價(jià)值偎快,兌換數(shù)量冯乘,用戶當(dāng)前積分) {
統(tǒng)計(jì)用戶禮品兌換次數(shù)()
判斷兌換數(shù)量,return
用戶積分更新(禮品價(jià)值*兌換數(shù)量滨砍,用戶當(dāng)前積分)
禮品數(shù)量更新(兌換數(shù)量)
}
1往湿、禮品數(shù)量更新妖异,并沒有判斷是否與之前的數(shù)量相等惋戏,因?yàn)橹灰怀l(fā)就可以。
2他膳、當(dāng)用戶進(jìn)行積分更新時(shí)响逢,where條件要判斷當(dāng)前的積分和之前查詢出的積分是否一致。這里其實(shí)對業(yè)務(wù)邏輯增加了一些限制棕孙。既用戶在進(jìn)行積分兌換時(shí)舔亭,不能執(zhí)行其它的操作。這個(gè)邏輯限制本身也符合真實(shí)業(yè)務(wù)場景蟀俊,對用戶操作體驗(yàn)沒有影響钦铺。
大家會(huì)發(fā)現(xiàn)瑞很多技術(shù)問題在網(wǎng)上都有現(xiàn)成的答案,并且是很多年以前的答案肢预。但是在程序中還會(huì)犯一樣的錯(cuò)誤矛洞,也許是我們老了,新一代的程序員在成長過程還需要不斷的重新摸索烫映。