一. 背景
表數據大概幾十萬,不到五十萬
每隔10幾分鐘使用truncate清空數據,然後再寫入等量數據
數據庫主從結構
數據庫版本5.7
二. 現象
執行truncate時從庫偶發拋異常【System Lock】,非必現。佔用線程資源,導致後續SQL語句全部阻塞
從庫不可用,進而導致路由到該從庫的請求全部阻塞,從而導致應用雪崩
三. 分析
網上搜了很多資料,都說是truncate在MySQL5.7及以下版本的bug,drop命令可以解決此問題,升級到8.0也可以解決此問題。
網上資料說數據量達到幾千萬纔會出現此問題,實際上幾十萬就出現。原因可能和相關配置、系統併發量有關
MDL寫鎖原因(猜測?)
- 參考資料:https://time.geekbang.org/column/article/69862
- 資料中說:對錶的增刪改查會加MDL讀鎖(表級鎖);對錶結構做變更會加寫鎖(表級鎖)(DDL語句),讀寫鎖/寫鎖之間互斥。
- truncate屬於DDL語句,即執行後會加MDL寫鎖(表級鎖),其它對錶的操作都會堵塞(包括查詢),而由於truncate的一些機制(可能是bug)導致一直無法釋放鎖(偶發現象),結果導致【System Lock】
四. 解決思路
MySQL版本:5.7
表名:tb1
思路:使用drop 替代 truncate
方案一
- drop tb1_temp(如果存在)
- 重命名tb1爲tb1_temp
- 創建新表tb1
- 寫入數據到tb1
- drop tb1_temp
方案二(選中)
- drop tb1_new,tb1_bak(如果存在就刪除)
- 創建表tb1_new
- 寫入數據到tb1_new
- 重命名tb1到tb1_bak
- 重命名tb1_new到tb1
- drop tb1_bak
五. 結論
最終選中方案二
兩者的區別在於
- 方案二的數據不一致及表不可以用的時間點在(4,5)兩步。只涉及表重命名,時間較短
- 方案一的數據不一致及表不可以用的時間點在(2,3,4)兩步。涉及到寫入數據,時間較長