InnoDB中鎖的查看
阿新 • • 發佈:2018-06-03
sql hold res code ica sys 語句 tro tips
Ⅰ、 show engine innodb status\G
1.1 實力分析一波
鎖介紹的那篇中已經提到了這個命令,現在我們開一個參數,更細致的分析一下這個命令
(root@localhost) [(none)]> set global innodb_status_output_locks=1;
Query OK, 0 rows affected (0.00 sec)
(root@localhost) [test]> begin; Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> delete from l where a = 2; Query OK, 1 row affected (0.00 sec) (root@localhost) [test]> update l set b = b + 1 where a = 4; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 (root@localhost) [test]> show engine innodb status\G ... ------------ TRANSACTIONS ------------ Trx id counter 30217417 Purge done for trx‘s n:o < 30217417 undo n:o < 0 state: running but idle History list length 74 LIST OF TRANSACTIONS FOR EACH SESSION: ---TRANSACTION 30217412, ACTIVE 37 sec 2 lock struct(s), heap size 1136, 2 row lock(s), undo log entries 2 MySQL thread id 355, OS thread handle 140483080300288, query id 1263 localhost root starting show engine innodb status TABLE LOCK table `test`.`l` trx id 30217412 lock mode IX RECORD LOCKS space id 1358 page no 3 n bits 72 index PRIMARY of table `test`.`l` trx id 30217412 lock_mode X locks rec but not gap Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 32 0: len 4; hex 80000002; asc ;; 1: len 6; hex 000001cd14c4; asc ;; 2: len 7; hex 2400000fc21499; asc $ ;; 3: len 4; hex 80000004; asc ;; 4: len 4; hex 80000006; asc ;; 5: len 4; hex 80000008; asc ;; Record lock, heap no 3 PHYSICAL RECORD: n_fields 6; compact format; info bits 0 0: len 4; hex 80000004; asc ;; 1: len 6; hex 000001cd14c4; asc ;; 2: len 7; hex 2400000fc214c8; asc $ ;; 3: len 4; hex 80000007; asc ;; 4: len 4; hex 80000008; asc ;; 5: len 4; hex 8000000a; asc ;; ...
解析:
- table lock IX 意向排他鎖(意向鎖都是表鎖)
- record locks 記錄鎖
-->space id 表空間
-->page no 第幾個頁,所有的記錄開始寫都是從表的第四個頁開始寫,第四個頁也是聚集索引的root page
-->index PRIMARY 表示在主鍵上加了一把鎖
-->lock_mode 鎖的模式
-->locks rec but not gap 這個先不看
-->heap no 2 PHYSICAL RECORD: n_fields 6 鎖住記錄的heap no為2的物理記錄,這個記錄一共6個列
-->compact format 這條記錄的存儲格式是compact(dynamic也是compact)
-->info bits 0表示這條記錄沒有被刪除;非0表示被修改或者被刪除(32)
Q? 表中是四個列,為什麽這把是6個列?
- 如果沒有主鍵的話,會多一個隱藏列row_id,這裏有主鍵row_id就是主鍵那不談
- 6個字節的表示事務id,7個字節表示回滾指針,這兩個列就是隱藏列
1.2、趁熱打鐵,分析一下等待的情況
session1:
(root@localhost) [test]> begin;
Query OK, 0 rows affected (0.00 sec)
(root@localhost) [test]> delete from l where a = 2;
Query OK, 1 row affected (0.00 sec)
session2:
(root@localhost) [test]> begin; Query OK, 0 rows affected (0.00 sec) (root@localhost) [test]> select * from l where a=2 for update; hang住了
session3:
...
------------
TRANSACTIONS
------------
Trx id counter 30217456
Purge done for trx‘s n:o < 30217442 undo n:o < 0 state: running but idle
History list length 4
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421958478909040, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 30217455, ACTIVE 1741 sec starting index read
mysql tables in use 1, locked 1
LOCK WAIT 2 lock struct(s), heap size 1136, 2 row lock(s)
MySQL thread id 396, OS thread handle 140483215816448, query id 2340 localhost root statistics
select * from l where a=2 for update
------- TRX HAS BEEN WAITING 27 SEC FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 1358 page no 3 n bits 72 index PRIMARY of table `test`.`l` trx id 30217455 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 32
0: len 4; hex 80000002; asc ;;
1: len 6; hex 000001cd14ee; asc ;;
2: len 7; hex 230000013d27d5; asc # =‘ ;;
3: len 4; hex 80000004; asc ;;
4: len 4; hex 80000006; asc ;;
5: len 4; hex 80000008; asc ;;
------------------
TABLE LOCK table `test`.`l` trx id 30217455 lock mode IX
RECORD LOCKS space id 1358 page no 3 n bits 72 index PRIMARY of table `test`.`l` trx id 30217455 lock_mode X locks rec but not gap waiting
Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 32
0: len 4; hex 80000002; asc ;;
1: len 6; hex 000001cd14ee; asc ;;
2: len 7; hex 230000013d27d5; asc # =‘ ;;
3: len 4; hex 80000004; asc ;;
4: len 4; hex 80000006; asc ;;
5: len 4; hex 80000008; asc ;;
---TRANSACTION 30217454, ACTIVE 1821 sec
2 lock struct(s), heap size 1136, 1 row lock(s), undo log entries 1
MySQL thread id 355, OS thread handle 140483080300288, query id 2339 localhost root
TABLE LOCK table `test`.`l` trx id 30217454 lock mode IX
RECORD LOCKS space id 1358 page no 3 n bits 72 index PRIMARY of table `test`.`l` trx id 30217454 lock_mode X locks rec but not gap
Record lock, heap no 2 PHYSICAL RECORD: n_fields 6; compact format; info bits 32
0: len 4; hex 80000002; asc ;;
1: len 6; hex 000001cd14ee; asc ;;
2: len 7; hex 230000013d27d5; asc # =‘ ;;
3: len 4; hex 80000004; asc ;;
4: len 4; hex 80000006; asc ;;
5: len 4; hex 80000008; asc ;;
...
- 找到LOCK WAIT
LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s) 兩個鎖結構,一個記錄鎖 - 找到TRX HAS BEEN WAITING 27 SEC FOR THIS LOCK TO BE GRANTED
等的是主鍵是2的這條記錄上的鎖,鎖的類型是排他鎖 再往下看,找到hold住2這條記錄的事務,根據thread id 355可以找到對應的線程
這個355就是show processlist;對應的id,我們去session1上看下便知(root@localhost) [test]> show processlist; +-----+------+-----------+------+---------+------+----------+------------------+ | Id | User | Host | db | Command | Time | State | Info | +-----+------+-----------+------+---------+------+----------+------------------+ | 355 | root | localhost | test | Query | 0 | starting | show processlist | | 396 | root | localhost | test | Sleep | 1321 | | NULL | +-----+------+-----------+------+---------+------+----------+------------------+ 2 rows in set (0.00 sec) 註意再thread_id表中就不一樣了,是對應proceelist_id (root@localhost) [test]> select thread_id,processlist_id,thread_os_id from performance_schema.threads where processlist_id is not NULL; +-----------+----------------+--------------+ | thread_id | processlist_id | thread_os_id | +-----------+----------------+--------------+ | 27 | 1 | 10574 | | 381 | 355 | 18745 | | 422 | 396 | 10592 | +-----------+----------------+--------------+ 3 rows in set (0.00 sec) 分別表示內部線程號(自增的),對應show processlist裏的id,進程號
Ⅱ、簡單點,上面是不是太專業了
2.1 利用三張表寫一個sql腳本
重復之前的步驟,一邊開一個事務刪除2這條記錄不提交,另一邊用for update查2這條記錄
(root@localhost) [(none)]> SELECT
-> r.trx_id waiting_trx_id,
-> r.trx_mysql_thread_id waiting_thread,
-> r.trx_query wating_query,
-> b.trx_id blocking_trx_id,
-> b.trx_mysql_thread_id blocking_thread,
-> b.trx_query blocking_query
-> FROM
-> information_schema.innodb_lock_waits w
-> INNER JOIN
-> information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id
-> INNER JOIN
-> information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;
+----------------+----------------+--------------------------------------+-----------------+-----------------+----------------+
| waiting_trx_id | waiting_thread | wating_query | blocking_trx_id | blocking_thread | blocking_query |
+----------------+----------------+--------------------------------------+-----------------+-----------------+----------------+
| 30217455 | 396 | select * from l where a=2 for update | 30217454 | 355 | NULL |
+----------------+----------------+--------------------------------------+-----------------+-----------------+----------------+
1 row in set, 1 warning (0.02 sec)
2.2 走sys庫看一把,更簡單
5.7才有sys庫,不過5.6也可以自行把sys庫弄進去
(root@localhost) [(none)]> select * from sys.innodb_lock_waits\G
*************************** 1. row ***************************
wait_started: 2018-06-03 00:52:01
wait_age: 00:00:14
wait_age_secs: 14
locked_table: `test`.`l`
locked_index: PRIMARY
locked_type: RECORD
waiting_trx_id: 30217455
waiting_trx_started: 2018-06-03 00:11:13
waiting_trx_age: 00:41:02
waiting_trx_rows_locked: 5
waiting_trx_rows_modified: 0
waiting_pid: 396
waiting_query: select * from l where a=2 for update
waiting_lock_id: 30217455:1358:3:2
waiting_lock_mode: X
blocking_trx_id: 30217454
blocking_pid: 355
blocking_query: NULL
blocking_lock_id: 30217454:1358:3:2
blocking_lock_mode: X
blocking_trx_started: 2018-06-03 00:09:53
blocking_trx_age: 00:42:22
blocking_trx_rows_locked: 1
blocking_trx_rows_modified: 1
sql_kill_blocking_query: KILL QUERY 355
sql_kill_blocking_connection: KILL 355
1 row in set, 3 warnings (0.09 sec)
tips:
- waiting_lock_id: 30217455:1358:3:2 這個東西表示 事務ID:space:page_No:heap_no,其他得比較簡單不用說了
- blocking_query是null,waiting_query是知道的,為什麽?
因為blocking的語句已經執行結束了,只是事務沒提交罷了
線上大部分時間是看不到這個blocking_query的
即使show engine innodb status\G也是只能看到在等待哪條記錄上的鎖釋放,而看不到是哪條sql導致的這個問題 - 最下面的KILL QUERY和KILL的區別是?
KILL QUERY是殺這個查詢,KILL是直接殺連接
Ⅲ、鎖超時
剛才模擬鎖等待過程中出現了下面得報錯
(root@localhost) [test]> select * from l where a=2 for update;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
這叫鎖等待超時,開發人員通常把這個和死鎖混為一談
lock持有的時間是以事務為單位的,事務提交後才會把事務裏所有的鎖釋放,這是無法避免的,不過可以通過一個參數來控制超時時間
(root@localhost) [test]> show variables like ‘innodb_lock_wait_timeout‘;
+--------------------------+-------+
| Variable_name | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 50 |
+--------------------------+-------+
1 row in set (0.00 sec)
默認50s,建議設置為3s左右即可
InnoDB中鎖的查看