正常使用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