一、背景
今天遇到了一個很奇葩的bug,是這樣的。我在一個service里面執行了4步操作(流程圖如下):
1)給表1新增了一條數據;
2)根據表1新增的結果,給表2新增一條數據;
3)根據表2插入的結果,異步調用第三方接口。然后他們處理完了之后以MQ的形式告訴我的服務;
4)如果調用第三方接口成功,繼續處理剩下的邏輯。
遇到的問題是:我收到第三方的MQ后,有時查表1中新增的數據查不到,但是數據庫里面明明有的啊。另外,為啥偶爾查不到呢?難道有代碼玄學在作怪么?

二、原因
究其原因,是 數據庫事務在作怪,也怪我當初學習的時候只是背了概念,沒有深刻的去理解它。當時代碼上面有有一個注解@Transactional,看了它的源碼,不聲明隔離級別時,它是使用數據庫默認的隔離級別。我們知道mysql默認的隔離級別時“可重復讀” (讀未提交——讀已提交——可重復讀——串行化)。所以就清楚了,當收到第三方的MQ之后,由於之前的事務沒有提交,所以當然查不到數據了。
@Transactional(rollbackFor = {RuntimeException.class, OptimusExceptionBase.class})
@Override
public void xxx(){
//邏輯處理
}
/** * Use the default isolation level of the underlying datastore. * All other levels correspond to the JDBC isolation levels. * @see java.sql.Connection */ Isolation isolation() default Isolation.DEFAULT;
三、解決辦法
方案一:在收到MQ的那個service里面,可以將隔離級別設置為“讀未提交”;
方案二:在調用第三方的那個service里面,去掉事務,這樣只要有數據庫的“寫操作”,則立馬會得到數據庫的響應,不會受后續代碼寫數據庫的影響,完全是單獨的。當然缺點是需要自己控制事務的執行與回滾;
方案三:可能情況下,調整代碼流程,將調用第三方的操作放在最后,調用第三方結束則方法結束。
四、其他情況
還有一種情況也會發生“插入數據,讀取不到”。當數據庫配置了讀寫分離后,寫數據庫會寫主庫,而讀數據會讀從庫。這樣在網絡延遲比較高的情況下,可能發生主從延遲——在主庫新插入的數據還沒有同步到從庫,導致查詢從庫查詢不到。對於這種問題,解決辦法就好多了,具體用那種根據自己的業務決定:
1)降低主從延遲;
2)優化代碼邏輯,為啥插入了還要讀取;
3)開啟半同步復制,即數據同步到了從庫才算真正的插入。
