1. 程式人生 > 實用技巧 >Insert into select引起死鎖問題分析

Insert into select引起死鎖問題分析

正常使用Insert into select去遷移資料:

INSERT INTO order_record SELECT  
    *   
FROM  
    order_today   
WHERE  
    pay_success_time < '2020-03-08 00:00:00'; 

因為是在生產環境直接備份資料,所以有加上“pay_success_time < '2020-03-08 00:00:00'”這個條件(因為歷史資料是不會再改動的),這條sql看似沒有任何問題,但是卻導致線上很多資料出現新增,修改失敗。這到底是什麼原因導致的。我們先來了解下Insert into select的工作原理,在預設的事務隔離級別下:insert into order_record select * from order_today

加鎖規則是:order_record表鎖,order_today逐步鎖(掃描一個鎖一個)。從這裡我們可以看出,在執行備份的時候,會導致order_today表被逐步鎖定,知道備份到最後全表鎖定。由於鎖定的資料越來越多,就導致出現了大量資料插入失敗。最後全部鎖住,導致無法插入資料。

那我們要如何避免這類問題的發生呢?

由於查詢條件會導致order_today全表掃描,什麼能避免全表掃描呢,很簡單嘛,給pay_success_time欄位新增一個idx_pay_suc_time索引就可以了,由於走索引查詢,就不會出現掃描全表的情況而鎖表了,只會鎖定符合條件的記錄。當然這也必須保證在歷史資料不會被更改的情況下。

修改後的sql:

INSERT INTO order_record SELECT  
    *   
FROM  
    order_today FORCE INDEX (idx_pay_suc_time)  
WHERE  
    pay_success_time <= '2020-03-08 00:00:00';  

最後總結:

使用insert into tablA select * from tableB語句時,一定要確保tableB後面的whereorder或者其他條件,都需要有對應的索引,來避免出現tableB全部記錄被鎖定的情況。

參考:

參考文章:

https://blog.csdn.net/asdfsadfasdfsa/article/details/83030011