現象
這次版本發完生產不久,主管就在羣裏通知了數據庫有性能問題,產生了死鎖。
這條SQL一直沒有執行結束,導致系統其它服務都出現性能問題。
背景
這是一條更新人員索引日誌表的SQL,這張表的作用就是用來標記哪些人員的數據發生變化。
涉及這條SQL的模塊有後臺任務和操作人員數據的功能。
後臺任務從這張表獲取需要更新的人員,將數據同步到solr。
操作人員數據的功能需要在這張表中標記該人員爲待更新。
分析
產生問題的可能分爲兩個模塊:一個是後臺任務,一個操作人員數據的功能。
這兩個模塊如果同時更新同一個人員,並且一方的執行時間比較長,事務未提交,而另一方在此時提交事務,那就會產生問題。
後臺任務
邏輯不復雜,而且包含更新日誌表的操作的事務邏輯簡單,不存在耗時問題
操作人員數據的功能
拿審覈人員舉例,審覈人員包括一系列的流程:校驗參數、更新信息、更新日誌表、調用接口、發送郵件。
這一系列流程都是在同個事務中進行的,調用接口和發送郵件是比較耗時的操作。原先是沒啥問題的,但是後來加的更新日誌表,這個方法操作的表就是跟後臺任務更新人員索引操作的表是同一張表。
產生問題的過程
1、該人員在日誌表中的狀態爲未更新(其他操作導致狀態爲未更新)
2、正在執行審覈該人員的功能,由於耗時較長,事務還未提交
3、後臺任務遍歷到了該人員,準備更新狀態時,就出現了問題
解決方法
對Spring事務傳播行爲有所瞭解的朋友,應該就知道解決方法了。(Spring默認的事務傳播行爲是REQUIRED
)
既然由於審覈操作耗時太久,更新日誌表的事務未能及時提交,那就想方法把更新日誌表的事務先提交,別佔用太多時間。
我這裏使用的方法是在更新日誌表的方法上加上@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
,這裏的意思就是爲該方法開啓一個新的事務,當該方法結束時就提交事務。這樣一來這個簡單的方法就可以馬上提交對日誌表的操作,而不會因爲審覈操作太耗時而佔用太多時間。