Insert into select引起死鎖問題分析
阿新 • • 發佈:2021-01-20
正常使用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
後面的where
,order
或者其他條件,都需要有對應的索引,來避免出現tableB全部記錄被鎖定的情況。
參考:
參考文章:
https://blog.csdn.net/asdfsadfasdfsa/article/details/83030011