生產上遇到的一例mycat讀寫分離延時問題

問題描述

場景是這樣的,我們的支付系統在一筆支付完成後,需要發出通知給到商戶。支付完成的消息通過消息隊列發送給通知的服務。通知服務的有一部分處理邏輯是這樣的:

            notifyPersist.saveNotifyRecord(notifyRecord);
            notifyRecord = rpNotifyService.getNotifyByMerchantNoAndMerchantOrderNoAndNotifyType(notifyRecord.getMerchantNo(), notifyRecord.getMerchantOrderNo(), notifyRecord.getNotifyType());
            notifyQueue.addElementToList(notifyRecord);

三行代碼。我解釋下,通知服務收到消息解析成 notifyRecord 對象,然後存入數據庫,然後馬上取出添加到任務隊列。另外又一個獨立的線程去處理這個任務隊列。

項目上線後,客戶反饋偶爾會出現收不到通知的情況。

問題排查

經過日誌跟蹤,我發現是在上述代碼的第二行,查詢記錄的時候數據庫返回null,也就是沒有查詢到記錄。導致任務隊列沒有該筆支付的通知任務。

一開始覺得非常不解,因爲通過日誌我們發現第一行代碼是執行成功的,既然插入成功了,沒理由查詢不到啊??而且我去數據庫看確定是有這條記錄的。莫非是見鬼了!!

我先說下

慢慢靜下心來思考,結合這個現象是偶發性的,我想到有沒有可能是因爲讀寫分離延時造成的。我先說下我們的存儲架構:

centos 6.5 64位操作系統。mycat1.6版本,mysql 5.6.21

數據庫服務器有兩臺,一臺主,一臺從,利用mycat配置了主從複製和讀寫分離。寫操作在主機上,讀操作在從機上。如下圖所示:

在這裏插入圖片描述

有沒有可能是主庫上插入成功後,從庫還沒有來得急同步完成,應用就馬上查詢,所以查不到。爲了驗證我的想法,我把代碼改成下面這樣發佈到生產先看看效果:

notifyPersist.saveNotifyRecord(notifyRecord);
Thread.sleep(300);
notifyRecord = rpNotifyService.getNotifyByMerchantNoAndMerchantOrderNoAndNotifyType(notifyRecord.getMerchantNo(), notifyRecord.getMerchantOrderNo(), notifyRecord.getNotifyType());
notifyQueue.addElementToList(notifyRecord);

思路也很簡單粗暴,就是插入之後不馬上讀,而是等一會讓從庫同步完再讀。發佈後,跑了一個段時間,沒有反饋異常。證明我懷疑的沒錯,問題確實出現在mycat讀寫分離延時上。

解決方案

當然,上面定位問題的sleep也勉強算是一個解決方案。只不過感覺比較low,原理很好理解。在插入數據和查詢數據中間加一個sleep()方法,相當於等一會再讀。如果應用對時效要求不高,
此方法也不失唯一種快速有效的方案。

找到了問題的根源我就去mycat的官網和相關論壇尋找解決方案。

功夫不負有心人,後來我查到相關資料,mycat可以通過註解的方式指定某些 sql 語句強制走主庫,如下所示:

<select id="listBy" parameterType="java.util.Map" resultMap="BaseResultMap">
    /**mycat:db_type=master*/select * from rp_notify_record
    <where>
      <include refid="condition_sql" />
    </where>
    <![CDATA[ order by create_time desc]]>
  </select>

注意到 /** */裏面的註解,通過指定 db_type=master 保證後面的 sql 語句走主庫。這樣就不存在延時導致查詢不到的問題了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章