問題描述
項目中突然報錯,主要是兩個,一個是任務腳本執行時間過長,另一個是鎖等待時間過長,如下
Lock wait timeout exceeded; try restarting transaction
該任務腳本用於做多個表之間的數據同步,同時,項目中還有多個腳本和其他代碼有可能對錶數據進行操作,產生鎖。有兩個表的數據量爲百萬級別,且數據項也比較多。
如果對死鎖和鎖等待不是很瞭解,可以快速跳到下面的鏈接,理解一下什麼是鎖等待,以及可能造成的原因。死鎖和鎖等待
問題分析
首先,先放出來這部分的僞代碼吧(便於理解)
void method(){
事務開始;
while(true) {
List<ObjectA> list1 = 從A表按照條件拿出100條數據;
if(list1爲空){ //如果爲空,表示數據已經處理完畢,結束
return;
}
//依次操作ObjectA數據
for(ObjectA objectA: list1) {
刪除A表數據;
與B表數據合併,並更新對應的數據;
同步C表數據;
}
}
事務結束;
}
原因主要是事務加的位置有問題,並且數據表沒有對應索引,導致更新時間過長。
任務執行時間過長
這個主要是因爲更新數據的時候缺少索引,數據量比較大時,查找對應數據的時間比較長。這個可以通過數據庫sql語句執行時間分析出來,因爲我們單位有大數據平臺,通過大數據平臺的分析,找到執行時間最長的幾條sql分析一下就可以了。如果沒有相關的平臺,建議在一些數據量比較大的表的查詢和更新操作前後加日誌記錄,方便對sql執行情況進行分析。
鎖等待時間過長
這個是事務和索引共同導致的。
在事務執行的過程中,對事務中所有涉及更新/添加的表加鎖,其他進程請求這部分表數據時就會鎖等待。索引缺失導致代碼中每次循環的時候都過長,又對整個循環加上了事務,所以鎖等待的時間就會超時,出現文章開頭的問題。除了數據庫層面加上索引,還需要調整代碼的內容,去掉事務或者優化事務位置。
問題解決
1、數據庫:我們在b表,根據查詢的條件建立了索引。之前平均執行時間爲10min,添加後縮短到1min。
2、代碼,因爲我們業務特點,我們這段代碼,即使更新出現異常,只要依然保留A表數據,就不會對整體業務產生影響。下次腳本執行時就可以補充回來這部分數據,所以我們去掉事務、調整代碼結構並加上一段異常處理,調整後的代碼如下。
void method(){
while(true) {
List<ObjectA> list1 = 從A表按照條件拿出100條數據;
if(list1爲空){ //如果爲空,表示數據已經處理完畢,結束
return;
}
//依次操作ObjectA數據
for(ObjectA objectA: list1) {
try{
與B表數據合併,並更新對應的數據;
同步C表數據;
刪除A表數據;
} catch(Exception e) {
日誌記錄異常;
} finally {
日誌記錄執行時間;
}
}
}
}
當然,如果業務場景需要,必須使用事務,也可以把循環內的代碼拿出來,單獨進行事務操作。