1. 問題描述
我們的工程部署在兩個DC上,雙活。兩邊的服務器以及邏輯都是一樣的,也分別都有自己的業(yè)務。數(shù)據(jù)庫用的Oracle,之間有同步,用的是Quest的DB同步產(chǎn)品shareplex。shareplex的原理是,read進程讀取分析數(shù)據(jù)庫的redo log,把需要更新的數(shù)據(jù)放到queue里面,export進程讀取queue的數(shù)據(jù),發(fā)送到對端的queue里面,由對端的import進程寫入對端數(shù)據(jù)庫。
雖然兩個DC只接收存儲單邊的數(shù)據(jù),但是兩邊數(shù)據(jù)庫都是有完整的數(shù)據(jù)的。

然而最近發(fā)現(xiàn)奇怪的問題,兩邊的數(shù)據(jù)一直對不上,導致很多依賴數(shù)據(jù)完整性的功能全都錯亂了。
2. 問題定位
既然是數(shù)據(jù)不一致,那我們就從數(shù)據(jù)庫同步入手。我們聯(lián)系了DBA,同時檢查項目里面有沒有大量的增刪改操作。對并發(fā)量大的幾張表有修改的地方我們都過了一遍,發(fā)現(xiàn)并沒有什么可疑的地方。
等DBA回消息,果然,他們觀察到shareplex的復制隊列有大量的backlog。然而令我們沒想到的是,堵在隊列里的竟然是對一張數(shù)據(jù)量并不大的表A的操作。A是一張maintain表,記錄著哪些component正處在維護狀態(tài)。每條記錄都必須關聯(lián)一個ticket,ticket的狀態(tài)有New,InProgress,Done,Completed,Cancelled等等。一般來說這張表的數(shù)據(jù)只有幾萬條,怎么會有那么多增刪改操作在這上面呢?!
追溯到問題剛開始出現(xiàn)的那個時間節(jié)點,我們是上了一個新功能。簡單描述就是,因為我們需要保證maintain表A里的ticket狀態(tài)信息是最新的,我們新加了一個Task,定時從源頭同步ticket狀態(tài)。于是我們仔細看了這塊邏輯,看出了端倪。Code的邏輯是:
1. 從數(shù)據(jù)庫取出所有狀態(tài)不是Complete的數(shù)據(jù)
2. foreach處理,從源頭拿到當前數(shù)據(jù)對應的ticket的狀態(tài)信息
3. 執(zhí)行更新操作,偽代碼如下:update maintain set status = #{status} where ticket = #{ticket}
乍一看,好像是沒什么問題,但是一細想,我們的maintain數(shù)據(jù)跟ticket的關系是多對一的,也就是說,一個ticket可以跟好多的maintain數(shù)據(jù)關聯(lián)!于是乎,在這種情況下,每次的update操作都會更新多條數(shù)據(jù),而且會更新多次!如果有100條一樣的ticket,那就會執(zhí)行100*100也就是一萬次操作。如果是一萬條一樣的ticket呢,那就是一億次更新操作!而且還僅僅是單次的量,考慮到task是定時跑的,量級只能更多。shareplex是撐不住這么大的量的。
3. 問題解決
找到了問題,對癥下藥:
1. 只有在ticket狀態(tài)有變化的時候才去更新數(shù)據(jù)庫
2. 當更新完一個ticket的時候,把ticket放到Set里面,后續(xù)操作如果發(fā)現(xiàn)相同ticket已經(jīng)被更新過了,就直接跳過
4. 總結(jié)
1. 碰到問題時,要特別留意新增的功能。因為對于比較穩(wěn)定的項目來說,新增的功能出問題的概率要遠大于老功能。
2. 當遇到在循環(huán)里面更新數(shù)據(jù)庫的情況,要特別留意是不是會導致數(shù)據(jù)重復更新。這不僅無端增加了數(shù)據(jù)庫的壓力,而且可能給數(shù)據(jù)庫之間的復制帶來災難。