表重複更新引發的問題

1. 問題描述

我們的工程部署在兩個DC(Primary和GSB)上,雙活。兩邊的服務器以及邏輯都是一樣的,也分別都有自己的業務。數據庫用的Oracle,之間有同步,用的是Quest的DB同步產品shareplex。shareplex的原理是,read進程讀取分析數據庫的redo log,把需要更新的數據放到queue裏面,export進程讀取queue的數據,發送到對端的queue裏面,由對端的import進程寫入對端數據庫。

雖然兩個DC只接收存儲單邊的數據,但是兩邊數據庫都是有完整的數據的。

數據複製

然而最近發現奇怪的問題,兩邊的數據一直對不上,導致很多依賴數據完整性的功能全都錯亂了。

2. 問題定位

既然是數據不一致,那我們就從數據庫同步入手。我們聯繫了DBA,同時檢查項目裏面有沒有大量的增刪改操作。對併發量大的幾張表有修改的地方我們都過了一遍,發現並沒有什麼可疑的地方。

等DBA回消息,果然,他們觀察到shareplex的複製隊列有大量的backlog。然而令我們沒想到的是,堵在隊列裏的竟然是對一張數據量並不大的表A的操作。A是一張maintain表,記錄着哪些component正處在維護狀態。每條記錄都必須關聯一個ticket,ticket的狀態有New,InProgress,Done,Completed,Cancelled等等。一般來說這張表的數據只有幾萬條,怎麼會有那麼多增刪改操作在這上面呢?!

追溯到問題剛開始出現的那個時間節點,我們是上了一個新功能。簡單描述就是,因爲我們需要保證maintain表A裏的ticket狀態信息是最新的,我們新加了一個Task,定時從源頭同步ticket狀態。於是我們仔細看了這塊邏輯,看出了端倪。Code的邏輯是:

  1. 從數據庫取出所有狀態不是Complete的數據
  2. foreach處理,從源頭拿到當前數據對應的ticket的狀態信息
  3. 執行更新操作,僞代碼如下:update maintain set status = #{status} where ticket = #{ticket}

乍一看,好像是沒什麼問題,但是一細想,我們的maintain數據跟ticket的關係是多對一的,也就是說,一個ticket可以跟好多的maintain數據關聯!於是乎,在這種情況下,每次的update操作都會更新多條數據,而且會更新多次!如果有100條一樣的ticket,那就會執行100*100也就是一萬次操作。如果是一萬條一樣的ticket呢,那就是一億次更新操作!而且還僅僅是單次的量,考慮到task是定時跑的,量級只能更多。shareplex是撐不住這麼大的量的。

3. 問題解決

找到了問題,對症下藥:

  1. 只有在ticket狀態有變化的時候纔去更新數據庫
  2. 當更新完一個ticket的時候,把ticket放到Set裏面,後續操作如果發現相同ticket已經被更新過了,就直接跳過

4. 總結

  1. 碰到問題時,要特別留意新增的功能。因爲對於比較穩定的項目來說,新增的功能出問題的概率要遠大於老功能。
  2. 當遇到在循環裏面更新數據庫的情況,要特別留意是不是會導致數據重複更新。這不僅無端增加了數據庫的壓力,而且可能給數據庫之間的複製帶來災難。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章