一诗力、問(wèn)題描述
商城系統(tǒng)今天上線(xiàn)了一個(gè)功能烙心,用戶(hù)在商城中點(diǎn)擊商品詳情頁(yè)的去購(gòu)買(mǎi)按鈕時(shí),如果商品是有代金券優(yōu)惠活動(dòng)的商品选泻,則會(huì)先領(lǐng)券后計(jì)算商品金額冲粤。 這里面涉及到兩步操作:調(diào)用領(lǐng)券接口,領(lǐng)券成功后页眯,調(diào)用計(jì)算接口(計(jì)算商品價(jià)格接口)梯捕,計(jì)算接口內(nèi)部會(huì)調(diào)用代金券查券接口。遇到問(wèn)題是窝撵,發(fā)券后傀顾,立即調(diào)用代金券查詢(xún)代金券,查詢(xún)不到剛發(fā)的代金券碌奉,停幾秒中再去查短曾,就可以查到,這種情況大概率會(huì)復(fù)現(xiàn)赐劣。
很奇怪的問(wèn)題嫉拐,代金券系統(tǒng)已經(jīng)很久沒(méi)有修改過(guò)了,線(xiàn)上也未爆出過(guò)發(fā)了代金券而查詢(xún)不到的情況魁兼。
二婉徘、問(wèn)題排查
一開(kāi)始以為是數(shù)據(jù)庫(kù)主從延遲導(dǎo)致的,把代金券查詢(xún)接口日志打開(kāi)咐汞,發(fā)現(xiàn)dao從庫(kù)中查詢(xún)時(shí)是可以查到剛發(fā)送的券盖呼,但是進(jìn)行規(guī)則過(guò)濾后,代金券被過(guò)濾了化撕,添加詳細(xì)的日志几晤,發(fā)現(xiàn)代金券是被有效期校驗(yàn)給過(guò)濾了。
代金券的有效期校驗(yàn)是:當(dāng)前時(shí)間 >= 代金券創(chuàng)建時(shí)間 and 當(dāng)前時(shí)間 <= 代金券結(jié)束時(shí)間
代金券有效期一般都是從發(fā)券時(shí)間開(kāi)始植阴,到發(fā)券時(shí)間+7天锌仅,因此當(dāng)前時(shí)間必然<代金券結(jié)束時(shí)間章钾,那么就是當(dāng)前時(shí)間小于創(chuàng)建時(shí)間導(dǎo)致過(guò)濾,這就更奇怪了热芹,發(fā)券在前贱傀,查詢(xún)?cè)诤螅樵?xún)時(shí)伊脓,系統(tǒng)時(shí)間應(yīng)該是大于代金券創(chuàng)建時(shí)間的府寒,為什么會(huì)被過(guò)濾呢。
查看數(shù)據(jù)庫(kù)記錄报腔,發(fā)現(xiàn)一個(gè)奇怪的問(wèn)題,代金券createtime居然比updatetime多了1s:
createTime | updateTime |
---|---|
2021-07-01 14:18:42 | 2021-07-01 14:18:41 |
心理隱約感覺(jué)找到了方向株搔。繼續(xù)查看代金系統(tǒng)的日志,發(fā)現(xiàn)創(chuàng)建日志打印時(shí)間為:2021-07-01 14:18:41.529纯蛾, 而查詢(xún)代金券的時(shí)間為2021-07-01 14:18:41.611纤房。
這大概就找到問(wèn)題了,createTime入庫(kù)時(shí)間比系統(tǒng)時(shí)間多了1秒翻诉,導(dǎo)致2021-07-01 14:18:41.611 < 2021-07-01 14:18:42炮姨。那么為什么入庫(kù)時(shí)間會(huì)多1秒呢?
查看代金券createTime設(shè)值的代碼:
setCreateTime(new Date());
取的是當(dāng)前系統(tǒng)時(shí)間碰煌,而updateTime其值不是由java代碼設(shè)置的舒岸,而是設(shè)置了默認(rèn)值,也就時(shí)入庫(kù)時(shí)芦圾,會(huì)取mysql current time蛾派。
看下createtime和updatetime的sql定義:
`create_time` datetime(0) DEFAULT '0000-00-00 00:00:00' COMMENT '創(chuàng)建時(shí)間',
`last_update_time` timestamp(0) DEFAULT CURRENT_TIMESTAMP(0) ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '最后更新時(shí)間',
發(fā)現(xiàn)兩個(gè)字段的類(lèi)型居然不一致,createTime是datetime(0)類(lèi)型,表示時(shí)間會(huì)精確到秒个少,而updateTime是timestamp(0)洪乍。
百度一番,發(fā)現(xiàn)這居然是mysql的一個(gè)bug夜焦, 當(dāng)時(shí)間字段類(lèi)型為datetime時(shí)壳澳,如果時(shí)間毫秒數(shù)小于500時(shí),向下舍糊探,如果大于等于500時(shí)向上加1秒,也就是:
2021-07-01 14:18:41.400 會(huì)存為2021-07-01 14:18:41
2021-07-01 14:18:41.591 會(huì)存為2021-07-01 14:18:42
線(xiàn)上之所以一直未爆出這個(gè)問(wèn)題河闰,主要是沒(méi)有這種領(lǐng)券后科平,在極短時(shí)間(500ms內(nèi))就去查券的場(chǎng)景,而商城這個(gè)場(chǎng)景是自動(dòng)發(fā)券自動(dòng)查姜性,間隔時(shí)間基本上100ms以?xún)?nèi)瞪慧,才爆出這個(gè)問(wèn)題。
三部念、問(wèn)題解決
解決辦法有3個(gè):
- 查詢(xún)判斷時(shí)弃酌,取當(dāng)前時(shí)間時(shí)氨菇,加1s
- 存datetime類(lèi)型的字段時(shí),對(duì)Date類(lèi)型的值進(jìn)行處理妓湘,將毫秒置為000查蓉,比如2021-07-01 14:18:41.400 設(shè)置為2021-07-01 14:18:41.000
- 將mysql時(shí)間類(lèi)型改為datetime(3),或者timestamp類(lèi)型。
推薦第二種榜贴,因?yàn)榈?種并沒(méi)有解決根本問(wèn)題豌研,存入的數(shù)據(jù)本身就是錯(cuò)的,保不齊其他地方也會(huì)有日期判斷的地方唬党,如果數(shù)據(jù)存入的就是錯(cuò)的鹃共,其他地方判斷也會(huì)是錯(cuò)的。而第3種需要修改數(shù)據(jù)庫(kù)字段類(lèi)型驶拱,公司內(nèi)對(duì)數(shù)據(jù)量大的表進(jìn)行修改比較麻煩霜浴,而且時(shí)間精確到毫秒意義并不大,還會(huì)占用更多的空間蓝纲,不如修改兩行代碼來(lái)的快捷阴孟。