1. 程式人生 > 實用技巧 >SqlServer 如何將excel檔案資料匯入資料庫

SqlServer 如何將excel檔案資料匯入資料庫

從“訂單不存在”異常出發,窺探mysql資料庫redo日誌和binlog的一致性問題

問題緣起

公司的公共訂單服務線上有一個場景:上游業務系統監聽訂單庫binlog,如果有新訂單產生,那麼根據binlog中的訂單id欄位,來反查訂單服務的查詢介面,獲取該訂單的詳細資訊;

但在該服務上線後,訂單服務的報警群裡經常能發現一類由於該查詢引發的訂單不存在異常,然後立刻通過一次重試,又能查到該訂單了,最開始的想法是這些查詢一定是走到了從庫,是由於資料庫主從同步延遲的原因,沒有查到該訂單資訊;

失敗的修復

基於這個假設,訂單服務開放了一個查詢主庫的介面,在該介面的sql語句里加入強制查詢主庫的字首(能通過公司的dbproxy路由到主庫),並讓該上游業務系統走該介面;但是該流程上線後,並沒有發現該類錯誤有減少;

這就有點出乎意料了:上游系統收到mysql的binlog訊息後再去反查主庫,此時該條binlog的對應資料,可能會在資料庫中還沒落盤嗎?
在這裡插入圖片描述

資料庫內部的兩階段提交

要解答該問題,需要了解mysql的redo日誌刷盤以及binlog的產生順序。首先先簡單介紹一下redo日誌和binlog日誌;

我們知道mysql innodb引擎為了保證事務的永續性,用redo日誌做宕機後的資料恢復;而mysql的binlog日誌用於資料庫的主從資料同步。為了保證兩者的一致性,mysql內部用了二階段提交的方式進行日誌和資料的最終刷盤,大致的簡化流程如下:
在這裡插入圖片描述
可以看到發起提交事務後主要經過3個階段:

  1. redo日誌的prepare階段,在這個階段innodb會將undo, redo日誌進行刷盤;
  2. binlog的prepare和commit階段,這裡binlog的prepare其實什麼也沒有做,而在commit的時候重新整理binlog到磁碟,在這個時候,其實事務是已經確定要提交了(無論後面是否發生宕機);
  3. redo日誌的commit階段,這個時候會清除undo日誌,把redo日誌刷資料盤,也就是mysql的儲存資料真正落庫;

如果mysql在這個過程中發生宕機,會以宕機前binlog的commit階段是否完成作為判斷標準,如果該事務的xid已經存在於binlog當中,也就是之前的事務的binlog已經刷到磁碟中,那麼會向前恢復,用redo日誌進行刷資料盤,繼續沒有完成的第3步,如果binlog中沒有該事務的xid,則進行回滾;所以通過這種redolog和binlog的prepare-commit兩階段提交方式,mysql保證最終刷到磁碟的redolog和binlog的內容是一致的,這兩者的一致性也保證了主從資料的一致;

在這個過程中可以看到,binlog是先於事務提交產生的;所以剛才的問題:收到binlog訊息再去反查主庫,理論上是否會存在查不到資料的情況,答案是肯定的;

case排查

但是這只是一個理論上的可能性,可以從上圖看到,在binlog傳送之後,整個事務剩下的只有第三步:清除undo日誌,並把redo日誌刷資料盤;一般一次資料庫的操作都是ms內的,而在這個“訂單不存在”的異常場景中,上游系統接收到binlog訊息並反查資料的過程中,資料鏈路包含canal, ddmq等中介軟體,還會經過訂單中心服務,耗時會比一次資料庫的操作都要少嗎?

所以還是需要具體case具體分析:找到報訂單不存在的一個訂單id,排查該訂單所對應的下單日誌,發現該下單呼叫一共用了900多ms,而呼叫資料庫插入訂單表就用了700多ms,明顯是有異常;於是聯絡DBA同學,讓他們確認下在mysql服務端的實際執行時間,發現確實耗費了這麼長的時間,DBA同學反饋如下:
在這裡插入圖片描述
所以基本可以得出是因為磁碟的ioutil利用率有異常的升高,導致資料庫偶發出現耗時很長的寫入;可以推測,如果ioutils在redo日誌的commit階段異常升高,那麼redo日誌刷資料盤時會因io堵塞而耗時很久,而此時binlog已經落盤了,上游系統就出現了收到binlog來反查還未提交完成的資料的case,導致訂單中心出現“訂單不存在”的報錯。

驗證和總結

後續DBA同學在6月15日進行了主從切換,後續就再也沒有出現過因為此類問題而報的訂單不存在異常,這從側面印證了mysql binlog和redo日誌的上述二階段提交流程。

這個問題揭示了在innodb事務的提交過程中,binlog和redolog還是存在短暫不一致的階段(雖然很短),而在這次的case中,由於機器硬體產生的ioutil異常將這不一致的問題進行了放大,出現了上游業務的查詢失敗問題,雖然該case最終能通過重試解決,但更好的做法是:對於依賴訂單的上游業務系統,應該接入的是訂單服務傳送的業務訊息(如下完單後的訂單建立訊息),而儘量少依靠底層的binlog訊息;