經常會出現這樣的場景:有一張3億的表,現在要對這張表進行刪除1億行,於是有人開始運行
delete from table limit 100000000;
毫無疑問這是一個愚蠢的刪除方式,於是有人開始變更刪除方式:delete from table where id<100000000;
然而運行一段時間后,又發現批量刪除的效率可能會更高,所以kill掉了上一條運行了一段時間的sql,開始批量刪除, 由於是大sql,晚上點擊運行想第二天早上來看結果的DBA就會遺憾的發現新執行的sql被鎖給擋了回來,並沒有運行,導致浪費了一晚上的時間。
但是盲目的等待鎖釋放心里沒底,所以我們可以通過下面的方式計算出這個鎖什么時候能夠釋放,我們就可以使用表了。
場景:
一個巨大的delete語句 執行一小時后kill ,
show processlist出現killed進程 ,
不要盲目重啟! 重啟MySQL后進程消失但鎖依然存在!
重啟MySQL后進程消失但鎖依然存在,因為回滾還要繼續,這是mysql對數據的保護機制
通過下列語句查詢事務情況:
SELECT * FROM information_schema.INNODB_TRX\G
*************************** 1. row ***************************
trx_id: 715674773
trx_state: ROLLING BACK
trx_started: 2018-09-24 23:17:30
trx_requested_lock_id: NULL
trx_wait_started: NULL
trx_weight: 540574
trx_mysql_thread_id: 0
trx_query: NULL
trx_operation_state: NULL
trx_tables_in_use: 0
trx_tables_locked: 1
trx_lock_structs: 3
trx_lock_memory_bytes: 1136
trx_rows_locked: 2
trx_rows_modified: 540571 #代表鎖影響的行數,當數值為0時,鎖將會釋放
trx_concurrency_tickets: 0
trx_isolation_level: REPEATABLE READ
trx_unique_checks: 1
trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
trx_adaptive_hash_latched: 0
trx_is_read_only: 0
trx_autocommit_non_locking: 0
1 row in set (0.00 sec)
trx_rows_modified: 代表鎖影響的行數,當數值為0時,鎖將會釋放
查看表鎖信息
SELECT * FROM information_schema.INNODB_LOCKS
SELECT * FROM information_schema.INNODB_LOCK_waits
desc innodb_locks;
+————-+———————+——+—–+———+——-+
| Field | Type | Null | Key | Default | Extra |
+————-+———————+——+—–+———+——-+
| lock_id | varchar(81) | NO | | | |#鎖ID
| lock_trx_id | varchar(18) | NO | | | |#擁有鎖的事務ID
| lock_mode | varchar(32) | NO | | | |#鎖模式
| lock_type | varchar(32) | NO | | | |#鎖類型
| lock_table | varchar(1024) | NO | | | |#被鎖的表
| lock_index | varchar(1024) | YES | | NULL | |#被鎖的索引
| lock_space | bigint(21) unsigned | YES | | NULL | |#被鎖的表空間號
| lock_page | bigint(21) unsigned | YES | | NULL | |#被鎖的頁號
| lock_rec | bigint(21) unsigned | YES | | NULL | |#被鎖的記錄號
| lock_data | varchar(8192) | YES | | NULL | |#被鎖的數據
+————-+———————+——+—–+———+——-+
10 rows in set (0.00 sec)
desc innodb_lock_waits;
+——————-+————-+——+—–+———+——-+
| Field | Type | Null | Key | Default | Extra |
+——————-+————-+——+—–+———+——-+
| requesting_trx_id | varchar(18) | NO | | | |#請求鎖的事務ID
| requested_lock_id | varchar(81) | NO | | | |#請求鎖的鎖ID
| blocking_trx_id | varchar(18) | NO | | | |#當前擁有鎖的事務ID
| blocking_lock_id | varchar(81) | NO | | | |#當前擁有鎖的鎖ID
+——————-+————-+——+—–+———+——-+
4 rows in set (0.00 sec)
desc innodb_trx ;
+—————————-+———————+——+—–+———————+——-+
| Field | Type | Null | Key | Default | Extra |
+—————————-+———————+——+—–+———————+——-+
| trx_id | varchar(18) | NO | | | |#事務ID
| trx_state | varchar(13) | NO | | | |#事務狀態:
| trx_started | datetime | NO | | 0000-00-00 00:00:00 | |#事務開始時間;
| trx_requested_lock_id | varchar(81) | YES | | NULL | |#innodb_locks.lock_id
| trx_wait_started | datetime | YES | | NULL | |#事務開始等待的時間
| trx_weight | bigint(21) unsigned | NO | | 0 | |#
| trx_mysql_thread_id | bigint(21) unsigned | NO | | 0 | |#事務線程ID
| trx_query | varchar(1024) | YES | | NULL | |#具體SQL語句
| trx_operation_state | varchar(64) | YES | | NULL | |#事務當前操作狀態
| trx_tables_in_use | bigint(21) unsigned | NO | | 0 | |#事務中有多少個表被使用
| trx_tables_locked | bigint(21) unsigned | NO | | 0 | |#事務擁有多少個鎖
| trx_lock_structs | bigint(21) unsigned | NO | | 0 | |#
| trx_lock_memory_bytes | bigint(21) unsigned | NO | | 0 | |#事務鎖住的內存大小(B)
| trx_rows_locked | bigint(21) unsigned | NO | | 0 | |#事務鎖住的行數
| trx_rows_modified | bigint(21) unsigned | NO | | 0 | |#事務更改的行數
| trx_concurrency_tickets | bigint(21) unsigned | NO | | 0 | |#事務並發票數
| trx_isolation_level | varchar(16) | NO | | | |#事務隔離級別
| trx_unique_checks | int(1) | NO | | 0 | |#是否唯一性檢查
| trx_foreign_key_checks | int(1) | NO | | 0 | |#是否外鍵檢查
| trx_last_foreign_key_error | varchar(256) | YES | | NULL | |#最后的外鍵錯誤
| trx_adaptive_hash_latched | int(1) | NO | | 0 | |#
| trx_adaptive_hash_timeout | bigint(21) unsigned | NO | | 0 | |#
+—————————-+———————+——+—–+———————+——-+
22 rows in set (0.01 sec)
結論
結論:時間過長的update、delete等語句在kill之后會進行回滾操作,會鎖表,經常有人更換不同方式對大數據進行修改刪除,然而盲目的殺死正在長時間運行的進程后並不能馬上對表進行新的操作,后果只能是等待之前的操作回滾結束,本想用更快的方式操作表結果得不償失,所以還是建議選擇好對表修改操作方式然后一次運行,不再修改。
后續測試了innodb_force_recovery參數的修改:
結論:非緊急情況不允許把innodb_force_recovery修改成非0值!
https://blog.csdn.net/m0_37827567/article/details/91044989
補充:
今天又遇到一個新的情況:
當我在改一個小表的表結構時發現本應該瞬間完成的語句一直沒有執行成功,連接數據庫端查看發現此語句正在等待鎖釋放;
於是開始尋找加鎖的源頭
show processlist;
select * from information_schema.processlist where time>100 and command<>'sleep';
果然查到了一個刪除語句;
但是發現該語句已經被kill 狀態顯示為killed,並且已經執行了50000s+ 也就是幾天前了;(之所以沒有監控到是因為這個我的過濾條件寫的是where command=‘Query’ 所以這個killed進程就沒有捕捉到)
此時我使用上述查詢查找鎖記錄發現查找結果是空的
SELECT * FROM information_schema.INNODB_TRX\G
查詢show engine innodb status 也沒有找到rolling back關鍵字的大回滾事務;
(我中間還測了一下收回賬戶權限這個查詢會不會消失 ,然而和我推測的一樣,並沒有什么錘子用)
於是我決定重啟;既然沒有回滾想必重啟應該不會有什么大問題;
所以我就shutdown了一下,mysql登陸不了了,但是進程很奇怪的還在;我又手動kill -9 了一下;
然后重新啟動,成功啟動;
new結論:
遇到killed語句先
SELECT * FROM information_schema.INNODB_TRX\G
看看有沒有什么事務正在回滾或被鎖住
如果有就安心的等它回滾結束,暫時不要用這個表,如果非要用就新建一個別名表 在從庫把備份拿過來寫進去,讓程序先換個表讀取;
如果查詢沒有結果,再次確認一下show engine innodb status 有沒有正在回滾的事務 如果進程太多可以重定向出來:
mysql -u -p -P 3306 -e “show engine innodb status” >/tmp/status.txt
斜杠/查找是否存在關鍵字 rolling back
如果確定沒有回滾語句,就可以安心的重啟了 ;
————————————————
原文鏈接:https://blog.csdn.net/m0_37827567/java/article/details/82979767