記一次 MySQL 主從複製延遲的踩坑

hoohack發表於2017-07-16

最近開發中遇到的一個 MySQL 主從延遲的坑,記錄並總結,避免再次犯同樣的錯誤。

情景

一個活動資訊需要審批,審批之後才能生效。因為之後活動要編輯,編輯後也可能觸發審批,審批中展示的是編輯前的活動內容,考慮到欄位比較多,也要儲存審批活動的內容,因此設計採用了一張臨時表,審批中的活動寫進審批表(activity_tmp),審批通過之後才把真正的活動內容寫進活動表(activity)。表的簡要設計如下,這裡將活動內容欄位合併為content展示:

遇到的問題

當時是有編輯觸發審批的情況,發現審批通過之後活動內容是空的,於是開始追查問題的原因。這裡說一句,當程式出問題的時候,95%都是程式碼的問題,先不要去懷疑環境出問題。好好的查日誌,然後看看你的程式碼吧。

追查問題回溯

1、查activity_tmp表,發現當時提交審批的活動內容是正常的,而且狀態也更新為審批通過了,懷疑是寫入activity表失敗 2、查activity表,發現審批後的內容確實沒有寫入,懷疑是程式碼問題 3、檢視程式碼,程式碼邏輯沒看出問題,懷疑資料庫操作失敗,檢視日誌 4、日誌顯示,有一句insert語句的活動內容為空,活動內容來自上一個mysql執行的是select語句,把該select語句拿出來放到線上的備庫查詢,發現活動內容是存在的。執行時查詢為空,執行完畢後查詢時內容存在,初步懷疑是主從延遲問題。 5、報錯只是部分失敗,確定是主從延遲的問題。

當時的問題程式碼

這就是主從延遲出現的地方,update後,馬上get,這是主從複製架構上開發的一個大忌。

解決方案

這類問題的解決方案有兩種:

  • 修改程式碼邏輯
  • 修改系統架構

對於修改程式碼邏輯,鄙人有兩點見解:

  • 如果第二步獲取的資料不需要第一步更新的status欄位,那就先讀,然後再更新
  • 如果第二步獲取的資料需要依賴第一步的status欄位,那就在讀出來的時候先判斷是否為空,如果是空的,報錯,下一次重試。

總結

其實之前也聽到過這樣的例子,但是由於沒有親身經歷,所以只保留了一種理論上的記憶,實際上印象不深,經歷了這麼一次踩坑後,印象特別深刻,現在看到別人寫這樣的程式碼也能馬上發現並指出。還是自己親身去踩坑印象最深。

日誌很重要,詳細的日誌更重要。日誌要記錄有用的資訊,方便追查問題的時候去追溯問題的本質原因。我覺得日誌就應該儘量做成飛機中的黑匣子,幫助我們儲存“事故“發生時的所有相關資訊。

打賞支援我寫出更多好文章,謝謝!

打賞作者

打賞支援我寫出更多好文章,謝謝!

任選一種支付方式

記一次 MySQL 主從複製延遲的踩坑 記一次 MySQL 主從複製延遲的踩坑

相關文章