performance_schema practical application notes (three) - blocking and lock Troubleshooting

Finally, a series of common MySQL introduce four types of lock blocking issues - global read lock, metadata lock (MDL), table locks, row lock

 

First, the global read lock

Global read lock is usually a flush table with read lock; adding such statements, such statements usually get a consistent backup in a variety of backup tools. Or using the main switching device. Where there is the most difficult to troubleshoot, if the online system permissions constraints are not standardized, various accounts have authority RELOAD, read locks can be added to the global database. 

In versions prior to 5.7 MySQL, to troubleshoot who hold global read lock is often difficult to query directly to the useful data (innodb_locks table lock can only record the information in the database innodb layer level, and global read lock is a lock server layer, not queried), from 5.7 began offering table performance_schema.metadata_locks table records some of the locks Server information layers (including global read lock and lock MDL).

 

Let's find out who is holding global read lock using performance_schema through an example.

First, open the first session, the implementation of global read lock.

-- 执行加锁语句
flush table with read lock;
Query OK, 0 rows affected (0.00 sec)

-- 查询加锁线程的process id,后续排查过程好对应
root@localhost : sbtest 12:31:48> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 4 |
+-----------------+
1 row in set (0.00 sec)

Open a second session to execute arbitrary dml statement to update operations as an example.

-- 测试查询
select * from sbtest1 limit 1\G;
*************************** 1. row ***************************
id: 21
k: 2483476
c: 09279210219-37745839908-56185699327-79477158641-86711242956-61449540392-42622804506-61031512845-36718422840-11028803849
pad: 96813293060-05308009118-09223341195-19224109585-45598161848
1 row in set (0.00 sec)
ERROR: 
No query specified

-- 记下线程id
select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 5 |
+-----------------+
1 row in set (0.00 sec)

-- 测试更新
update sbtest1 set pad='xxx' where id=21;  -- 操作被阻塞

Open the third session, for investigation.

select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 16 |
+-----------------+
1 row in set (0.00 sec)

-- 查询processlist信息,只能看到processid为5的线程State为Waiting for global read lock,表示正在等待全局读锁
show processlist;
+----+-------+---------------------+--------+-------------+-------+---------------------------------------------------------------+------------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-------+---------------------+--------+-------------+-------+---------------------------------------------------------------+------------------------------------------+
| 3 | qfsys | 192.168.2.168:41042 | NULL | Binlog Dump | 11457 | Master has sent all binlog to slave; waiting for more updates | NULL |
| 4 | root | localhost | sbtest | Sleep | 234 | | NULL |
| 5 | root | localhost | sbtest | Query | 26 | Waiting for global read lock | update sbtest1 set pad='xxx' where id=21 |
| 16 | root | localhost | NULL | Query | 0 | starting | show processlist |
+----+-------+---------------------+--------+-------------+-------+---------------------------------------------------------------+------------------------------------------+
4 rows in set (0.00 sec)

-- 查询information_schema.innodb_locks、innodb_lock_waits、innodb_trx表,发现三个表均为空(因为全局读锁不是InnoDB层而是Server层的锁)
select * from information_schema.innodb_locks;
Empty set, 1 warning (0.00 sec)
select * from information_schema.innodb_lock_waits;
Empty set, 1 warning (0.00 sec)
select * from information_schema.innodb_trx\G
Empty set (0.00 sec)

-- 再使用show engine innodb status;查看一把(只需要看TRANSACTION段落即可),仍然无任何有用的锁信息
root@localhost : (none) 12:59:48> show engine innodb status;
......
------------
TRANSACTIONS
------------
Trx id counter 2527502
Purge done for trx's n:o < 2527500 undo n:o < 0 state: running but idle
History list length 3
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 422099353083504, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 422099353082592, not started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 422099353081680, not started
0 lock struct(s), heap size 1136, 0 row lock(s)

By conventional means query down without any useful information, this time, there are an estimated gdb debugging experienced veterans will begin using gdb, strace, pstack what MySQL command to view the call stack, what a thread information, but not for the C language basis of people, basically hieroglyphics.

From the 5.7 version, provides performance_schema.metadata_locks table lock information recording layer of various Server, query the table below we try.

-- 持锁会话通常有两个(但为同一个线程),特征如下:OBJECT_TYPE为global和commit、LOCK_TYPE为SHARED

select * from performance_schema.metadata_locks where OWNER_THREAD_ID!=sys.ps_thread_id(connection_id())\G;

*************************** 1. row ***************************
      OBJECT_TYPE: GLOBAL <---
    OBJECT_SCHEMA: NULL
      OBJECT_NAME: NULL
OBJECT_INSTANCE_BEGIN: 140621322913984
        LOCK_TYPE: SHARED  # 共享锁
    LOCK_DURATION: EXPLICIT  # 显式
      LOCK_STATUS: GRANTED  # 已授予
           SOURCE: lock.cc:1110
  OWNER_THREAD_ID: 94 # 持有锁的内部线程ID为94
   OWNER_EVENT_ID: 16  

*************************** 2. row ***************************
      OBJECT_TYPE: COMMITL <---
    OBJECT_SCHEMA: NULL
      OBJECT_NAME: NULL
OBJECT_INSTANCE_BEGIN: 140621322926064
        LOCK_TYPE: SHARED # 共享锁
    LOCK_DURATION: EXPLICIT  # 显式
      LOCK_STATUS: GRANTED # 已授予
           SOURCE: lock.cc:1194
  OWNER_THREAD_ID: 94  # 持有锁的内部线程ID为94
   OWNER_EVENT_ID: 16  

*************************** 3. row ***************************
      OBJECT_TYPE: GLOBAL
    OBJECT_SCHEMA: NULL
      OBJECT_NAME: NULL
OBJECT_INSTANCE_BEGIN: 140621391527216
        LOCK_TYPE: INTENTION_EXCLUSIVE  # 意向排它锁
    LOCK_DURATION: STATEMENT  # 语句
      LOCK_STATUS: PENDING  # 状态为pending,等待锁授予(正在被阻塞)
           SOURCE: sql_base.cc:3190
  OWNER_THREAD_ID: 95  # 被阻塞的内部线程ID为95
   OWNER_EVENT_ID: 38 
3 rows in set (0.00 sec)

-- 查看process id为4,5 各自对应的内部线程ID是多少
select sys.ps_thread_id(4);
+---------------------+
| sys.ps_thread_id(4) |
+---------------------+
| 94 |    # process id=4的线程对应的内部线程ID正好为94,说明就是process id=4的线程持有了全局读锁
+---------------------+
1 row in set (0.00 sec)

select sys.ps_thread_id(5);
+---------------------+
| sys.ps_thread_id(5) |
+---------------------+
| 95 |   # proces id=5的线程对应的内部线程正好是95,说明在等待全局读锁的就是process id=5的线程
+---------------------+
1 row in set (0.00 sec)

If a production environment, all these taken by show processlist information corresponding process id = 4 rows found in user, host, db information, largely determine what belongs to what business purposes, to find relevant personnel for clarification, the kill to kill, to discuss the way down in the future how to avoid this problem.

 

Second, find out who holds MDL lock

This problem is common in: execute large queries / large transaction uncommitted -> execute ddl statement / Backup -> Other sessions to perform read and write operations, a large number of waiting for metadata lock wait.

It will be described later saw sys system library has a schema_table_lock_wait (5.7.9 new) view, you can easily view the MDL waiting for information. See the definition of this view will find its data source is threads, metadata_locks, events_statements_current table under performance_schema. This first how we find out who holds MDL term use table locks under performance_schema.

 

mdl lock recording instruments corresponding to wait / lock / metadata / sql / mdl, not enabled by default, corresponding to consumers performance_schema.metadata_locks, setup_consumers only by the global configuration item global_instrumentation control, enabled by default.

UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
  
select * from performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl';
 
+----------------------------+---------+-------+
| NAME                       | ENABLED | TIMED |
+----------------------------+---------+-------+
| wait/lock/metadata/sql/mdl | YES     | NO    |
+----------------------------+---------+-------+
1 row in set (0.00 sec)

First, open two sessions, respectively, execute the following statement.

-- 会话1,显式开启一个事务,并执行一个update语句更新sbtest1表不提交
begin;
Query OK, 0 rows affected (0.00 sec)

update sbtest1 set pad='yyy' where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

-- 会话2,对sbtest1表执行DDL语句添加一个普通索引
alter table sbtest1 add index i_c(c);  -- 被阻塞

Also open a session 3, using the show processlist statement to query thread information can be found update statement is waiting MDL lock (Waiting for table metadata lock).

show processlist;
+----+------+-----------+--------+---------+------+---------------------------------+--------------------------------------+
| Id | User | Host      | db    | Command | Time | State                          | Info                                |
+----+------+-----------+--------+---------+------+---------------------------------+--------------------------------------+
| 92 | root | localhost | sbtest | Query  |  121 | Waiting for table metadata lock | alter table sbtest1 add index i_c(c) |
| 93 | root | localhost | NULL  | Query  |    0 | starting                        | show processlist                    |
| 94 | root | localhost | sbtest | Sleep  | 1078 |                                | NULL                                |
+----+------+-----------+--------+---------+------+---------------------------------+--------------------------------------+
3 rows in set (0.00 sec)

Table View query performance_schema.metadata_locks lock MDL Information

root@localhost : (none) 01:23:05> select * from performance_schema.metadata_locks where OWNER_THREAD_ID!=sys.ps_thread_id(connection_id())\G;
*************************** 1. row ***************************
      OBJECT_TYPE: TABLE
    OBJECT_SCHEMA: sbtest
      OBJECT_NAME: sbtest1
OBJECT_INSTANCE_BEGIN: 139886013386816
        LOCK_TYPE: SHARED_WRITE
    LOCK_DURATION: TRANSACTION
      LOCK_STATUS: GRANTED
          SOURCE: sql_parse.cc:5996
  OWNER_THREAD_ID: 136
  OWNER_EVENT_ID: 721

*************************** 2. row ***************************
      OBJECT_TYPE: GLOBAL
    OBJECT_SCHEMA: NULL
      OBJECT_NAME: NULL
OBJECT_INSTANCE_BEGIN: 139886348911600
        LOCK_TYPE: INTENTION_EXCLUSIVE
    LOCK_DURATION: STATEMENT
      LOCK_STATUS: GRANTED
          SOURCE: sql_base.cc:5497
  OWNER_THREAD_ID: 134
  OWNER_EVENT_ID: 4667

*************************** 3. row ***************************
      OBJECT_TYPE: SCHEMA
    OBJECT_SCHEMA: sbtest
      OBJECT_NAME: NULL
OBJECT_INSTANCE_BEGIN: 139886346748096
        LOCK_TYPE: INTENTION_EXCLUSIVE
    LOCK_DURATION: TRANSACTION
      LOCK_STATUS: GRANTED
          SOURCE: sql_base.cc:5482
  OWNER_THREAD_ID: 134
  OWNER_EVENT_ID: 4667

*************************** 4. row ***************************
      OBJECT_TYPE: TABLE
    OBJECT_SCHEMA: sbtest
      OBJECT_NAME: sbtest1
OBJECT_INSTANCE_BEGIN: 139886346749984
        LOCK_TYPE: SHARED_UPGRADABLE
    LOCK_DURATION: TRANSACTION
      LOCK_STATUS: GRANTED
          SOURCE: sql_parse.cc:5996
  OWNER_THREAD_ID: 134
  OWNER_EVENT_ID: 4669

*************************** 5. row ***************************
      OBJECT_TYPE: TABLE
    OBJECT_SCHEMA: sbtest
      OBJECT_NAME: sbtest1
OBJECT_INSTANCE_BEGIN: 139886348913168
        LOCK_TYPE: EXCLUSIVE
    LOCK_DURATION: TRANSACTION
      LOCK_STATUS: PENDING  <-- 被阻塞者
          SOURCE: mdl.cc:3891
  OWNER_THREAD_ID: 134
  OWNER_EVENT_ID: 4748
5 rows in set (0.00 sec)

We found 5 rows MDL lock records, the first row of the table sbtest.sbtest1 SHARED_WRITE lock, is GRANTED state, holding the thread 136 (corresponding to the process id 94). Subsequent row 4, there SHARED_UPGRADABLE sbtest.sbtest1 table, EXCLUSIVE lock, which is GRANTED SHARED_UPGRADABLE state, EXCLUSIVE in PENDING state, holding the thread 134 (corresponding to the process id 92), the thread 134 is waiting MDL described lock.

Through the above data, we know that the MDL 136 thread holds the lock, can be seen through the query results show processlist statement process id of the thread 94 has been a long time in sleep state, and can not see what this thread executes the statement.

Need to check it information_schema.innodb_trx table, confirm the existence of the thread of the transaction did not submit. The table was found by querying process id thread 94 (trx_mysql_thread_id = 94) does have a uncommitted transactions, but there is not much useful information.

select * from information_schema.innodb_trx\G;

*************************** 1. row ***************************
                trx_id: 2452892
            trx_state: RUNNING
          trx_started: 2018-01-14 01:19:25 <-- 事务开始时间
trx_requested_lock_id: NULL
      trx_wait_started: NULL
            trx_weight: 3
  trx_mysql_thread_id: 94  <-- 线程id
......
1 row in set (0.00 sec)

Although it is known that the transaction did not submit the thread 136 due to the occurrence of MDL 134 thread lock wait, but do not know what 136 thread is doing. We can certainly kill off 136 thread so that the thread to continue down the 134, but if you do not know what 136 thread execution statement, you can not find developers to optimize, next time you may encounter similar problems again.

We can also help performance_schema.events_statements_current event information table to query a thread is executing or last execution completion (where information is not necessarily reliable because the table for each thread currently executing and recording only once implementation of the recently complete event information statement, once this thread execute a new statement, the information will be covered):

select * from performance_schema.events_statements_current where thread_id=136\G;

*************************** 1. row ***************************
          THREAD_ID: 136
          EVENT_ID: 715
      END_EVENT_ID: 887
        EVENT_NAME: statement/sql/update
            SOURCE: socket_connection.cc:101
......
          SQL_TEXT: update sbtest1 set pad='yyy' where id=1
            DIGEST: 69f516aa8eaa67fd6e7bfd3352de5d58
        DIGEST_TEXT: UPDATE `sbtest1` SET `pad` = ? WHERE `id` = ? 
    CURRENT_SCHEMA: sbtest
.....
1 row in set (0.00 sec)

Performance_schema.events_statements_current query information from tables by SQL_TEXT field we can see what SQL statements that thread is executing Yes. If the production environment, now, you can find relevant developer representations, next encounter a similar statement must be submitted in time to avoid similar problems next time.

 

Third, find out who holds a table lock

Table lock corresponding instruments (wait / lock / table / sql / handler) enabled by default, corresponding to consumers performance_schema.table_handles, setup_consumers only by the global configuration item global_instrumentation control, enabled by default. Therefore performance_schema = ON can be set by default. Here we demonstrate by example how to find out who holds a table lock.

First, open the two sessions, the first session add an explicit table lock on the table, the second operation session DML statement is executed against the table

-- 会话1加表级锁
select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 18 |
+-----------------+
1 row in set (0.00 sec)

lock table sbtest1 read;
Query OK, 0 rows affected (0.00 sec)

-- 会话2对该表执行update更新
select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 19 |
+-----------------+
1 row in set (0.00 sec)

update sbtest1 set pad='xxx' where id=1; -- 被阻塞

Open the third session, use the show processlist statement to query thread information, find the update statement is waiting to lock MDL

root@localhost : (none) 02:40:14> show processlist;
+----+-------+---------------------+--------+-------------+-------+---------------------------------------------------------------+-----------------------------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-------+---------------------+--------+-------------+-------+---------------------------------------------------------------+-----------------------------------------+
| 3 | qfsys | 192.168.2.168:41042 | NULL | Binlog Dump | 18565 | Master has sent all binlog to slave; waiting for more updates | NULL |
| 18 | root | localhost | sbtest | Sleep | 67 | | NULL |
| 19 | root | localhost | sbtest | Query | 51 | Waiting for table metadata lock | update sbtest1 set pad='xxx' where id=1 |
| 20 | root | localhost | NULL | Query | 0 | starting | show processlist |
+----+-------+---------------------+--------+-------------+-------+---------------------------------------------------------------+-----------------------------------------+
4 rows in set (0.00 sec)

Might think, since it is waiting for MDL lock on it by a step to the investigation, inquiry performance_schema.metadata_locks table, the order of records on behalf of the lock is held in chronological order

select * from performance_schema.metadata_locks where OWNER_THREAD_ID!=sys.ps_thread_id(connection_id())\G;

*************************** 1. row ***************************
      OBJECT_TYPE: TABLE
    OBJECT_SCHEMA: sbtest
      OBJECT_NAME: sbtest1
OBJECT_INSTANCE_BEGIN: 140622530920576
        LOCK_TYPE: SHARED_READ_ONLY
    LOCK_DURATION: TRANSACTION
      LOCK_STATUS: GRANTED
           SOURCE: sql_parse.cc:5996
  OWNER_THREAD_ID: 113  -- 内部ID为113的线程被授予SHARED_READ_ONLY锁,持有该锁的线程不允许其他线程修改sbtest1表的数据
   OWNER_EVENT_ID: 11

*************************** 2. row ***************************
      OBJECT_TYPE: GLOBAL
    OBJECT_SCHEMA: NULL
      OBJECT_NAME: NULL
OBJECT_INSTANCE_BEGIN: 140620517607728
        LOCK_TYPE: INTENTION_EXCLUSIVE
    LOCK_DURATION: STATEMENT
      LOCK_STATUS: GRANTED
           SOURCE: sql_base.cc:3190
  OWNER_THREAD_ID: 114  -- 内部ID为114的线程被授予锁INTENTION_EXCLUSIVE,即意向锁IX
   OWNER_EVENT_ID: 12

*************************** 3. row ***************************
      OBJECT_TYPE: TABLE
    OBJECT_SCHEMA: sbtest
      OBJECT_NAME: sbtest1
OBJECT_INSTANCE_BEGIN: 140620517607824
        LOCK_TYPE: SHARED_WRITE
    LOCK_DURATION: TRANSACTION
      LOCK_STATUS: PENDING
           SOURCE: sql_parse.cc:5996
  OWNER_THREAD_ID: 114  -- 内部ID为114的线程正在等待SHARED_WRITE锁被授予
   OWNER_EVENT_ID: 12
3 rows in set (0.00 sec)

Investigation stalled, we know MDL lock very common for most of the operating table will be the first on the table plus MDL lock (lock according to information performance_schema.metadata_locks recorded in the table top is not used).

At this point you can try to check some table-level locking and find performance_schema .table_handles table below

select * from performance_schema.table_handles where OWNER_THREAD_ID!=0\G;

*************************** 1. row ***************************
      OBJECT_TYPE: TABLE
    OBJECT_SCHEMA: sbtest
      OBJECT_NAME: sbtest1
OBJECT_INSTANCE_BEGIN: 140622530923216
  OWNER_THREAD_ID: 113
   OWNER_EVENT_ID: 11
    INTERNAL_LOCK: NULL
    EXTERNAL_LOCK: READ EXTERNAL -- 发现内部ID为113的线程持有了sbtest1表的READ EXTERNAL表级锁,这也是内部ID为114的线程无法获取到MDL锁的原因
1 row in set (0.00 sec)  

As can be seen by the above query to the relevant data, 113 thread sbtest1 table explicitly add a table-level read lock, and for a long time in a sleep state, but we do not know what that thread is executing SQL statements, you can performance_schema.events_statements_current table query, as follows

select * from performance_schema.events_statements_current where thread_id=113\G;
*************************** 1. row ***************************
          THREAD_ID: 113
           EVENT_ID: 10
       END_EVENT_ID: 10
         EVENT_NAME: statement/sql/lock_tables
             SOURCE: socket_connection.cc:101
        TIMER_START: 18503556405463000
          TIMER_END: 18503556716572000
         TIMER_WAIT: 311109000
          LOCK_TIME: 293000000
           SQL_TEXT: lock table sbtest1 read  -- 这里可以看到,内部ID为113的线程对表sbtest1执行了加读锁语句
             DIGEST: 9f987e807ca36e706e33275283b5572b
        DIGEST_TEXT: LOCK TABLE `sbtest1` READ 
     CURRENT_SCHEMA: sbtest
......
1 row in set (0.00 sec)

If a production environment, you can find relevant developer confirmed that, if no special operation, you can try to kill this thread.

How to know the internal ID 113 corresponds to the process id is how much? You can query by performance_schema.threads table. Note that 113 is not a process id, do Freeze kill.

select processlist_id from performance_schema.threads where thread_id=113;
+----------------+
| processlist_id |
+----------------+
| 18 |
+----------------+
1 row in set (0.00 sec)

kill 18;
Query OK, 0 rows affected (0.00 sec)

show processlist;
+----+-------+---------------------+--------+-------------+-------+---------------------------------------------------------------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-------+---------------------+--------+-------------+-------+---------------------------------------------------------------+------------------+
| 3 | qfsys | 192.168.2.168:41042 | NULL | Binlog Dump | 18994 | Master has sent all binlog to slave; waiting for more updates | NULL |
| 19 | root | localhost | sbtest | Sleep | 480 | | NULL |
| 20 | root | localhost | NULL | Query | 0 | starting | show processlist |
+----+-------+---------------------+--------+-------------+-------+---------------------------------------------------------------+------------------+
3 rows in set (0.00 sec)

-- 返回执行update语句的会话2,语句已经执行成功
update sbtest1 set pad='xxx' where id=1;
Query OK, 0 rows affected (7 min 50.23 sec)
Rows matched: 0 Changed: 0 Warnings: 0

 

Fourth, find out who holds row-level locking

performance_schema.data_lock table involved in the case in MySQL 8.0 is new, versions prior to 8.0 do not support here only as an extension of learning for the MySQL performance_schema 5.7.

If a transaction is uncommitted time, although you can query from information_schema.innodb_trx, performance_schema.events_transactions_current and other table to the appropriate transaction information, but they do not know what this transaction holding the lock. Although information_schema.innodb_locks table is used to record the transaction lock information, but the table will record lock wait for the lock information needs to occur in the transaction.

8.0 from the beginning, there is provided in the lock performance_schema a table for recording information on a data_locks any transaction (and discarding the information_schema.innodb_locks table), there is a relationship does not need to wait for a lock (note that this table only records storage engine innodb layer lock).

 

First, we open a session (session 1) In 8.0, explicitly open a transaction

select * from t_luoxiaobo limit 1;
+----+------+---------------------+
| id | test | datet_time          |
+----+------+---------------------+
|  2 | 1    | 2017-09-06 01:11:59 |
+----+------+---------------------+
1 row in set (0.00 sec)

begin;
Query OK, 0 rows affected (0.00 sec)

update t_luoxiaobo set datet_time=now() where id=2;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Open another session (session 2) query data_locks table

select * from data_locks\G;
*************************** 1. row ***************************
          ENGINE: INNODB
  ENGINE_LOCK_ID: 55562:62
ENGINE_TRANSACTION_ID: 55562
        THREAD_ID: 54  # 持有线程内部ID
        EVENT_ID: 85
    OBJECT_SCHEMA: xiaoboluo # 库名
      OBJECT_NAME: t_luoxiaobo  # 表名
  PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
      INDEX_NAME: NULL  # 索引名称
OBJECT_INSTANCE_BEGIN: 140439793477144
        LOCK_TYPE: TABLE  # 表级锁
        LOCK_MODE: IX  # IX锁
      LOCK_STATUS: GRANTED  # 被授予状态
        LOCK_DATA: NULL

*************************** 2. row ***************************
          ENGINE: INNODB
  ENGINE_LOCK_ID: 55562:2:4:2
ENGINE_TRANSACTION_ID: 55562
        THREAD_ID: 54  # 持有锁线程内部ID
        EVENT_ID: 85
    OBJECT_SCHEMA: xiaoboluo  # 库名
      OBJECT_NAME: t_luoxiaobo #表名
  PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
      INDEX_NAME: PRIMARY  # 索引为主键
OBJECT_INSTANCE_BEGIN: 140439793474104
        LOCK_TYPE: RECORD  # 行锁
        LOCK_MODE: X  # 排它锁
      LOCK_STATUS: GRANTED  # 被授予状态
        LOCK_DATA: 2  # 被锁定的数据记录,这里的记录对应的是 INDEX_NAME: PRIMARY 的value
2 rows in set (0.00 sec)

You can see from the results, there are two lines of the locks on the first line is t_luoxiaobo IX locks on the table, second line using the primary key index of X locks record locking, states are GRANTED 

Now, we simulated two scenarios occur DML lock waits. Opening a new session (session 3), in the case of a transaction session uncommitted, the session table 3 performs the same operation t_luoxiaobo

begin;
Query OK, 0 rows affected (0.00 sec)

update t_luoxiaobo set datet_time=now() where id=2;  -- 被阻塞

Back to the session 2 queries data_locks table, found four row lock records

select * from performance_schema.data_locks\G;
*************************** 1. row ***************************
......
        THREAD_ID: 55
......
        LOCK_TYPE: TABLE
        LOCK_MODE: IX
      LOCK_STATUS: GRANTED
        LOCK_DATA: NULL
*************************** 2. row ***************************
          ENGINE: INNODB
  ENGINE_LOCK_ID: 55563:2:4:2
ENGINE_TRANSACTION_ID: 55563
        THREAD_ID: 55  # 内部线程ID
        EVENT_ID: 8
    OBJECT_SCHEMA: xiaoboluo
      OBJECT_NAME: t_luoxiaobo
  PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
      INDEX_NAME: PRIMARY  # 发生锁的索引名称
OBJECT_INSTANCE_BEGIN: 140439793480168
        LOCK_TYPE: RECORD  # 记录锁
        LOCK_MODE: X  # 排它锁
      LOCK_STATUS: WAITING  # 正在等待锁被授予
        LOCK_DATA: 2 # 锁定的索引value,这里与内部ID为54线程持有的主键值为2的X锁完全一样,说明这里就是被内部ID为54线程阻塞了
*************************** 3. row ***************************
......
        THREAD_ID: 54
.......
        LOCK_TYPE: TABLE
        LOCK_MODE: IX
      LOCK_STATUS: GRANTED
        LOCK_DATA: NULL
*************************** 4. row ***************************
......
        THREAD_ID: 54
        EVENT_ID: 85
    OBJECT_SCHEMA: xiaoboluo
      OBJECT_NAME: t_luoxiaobo
  PARTITION_NAME: NULL
SUBPARTITION_NAME: NULL
      INDEX_NAME: PRIMARY
OBJECT_INSTANCE_BEGIN: 140439793474104
        LOCK_TYPE: RECORD
        LOCK_MODE: X
      LOCK_STATUS: GRANTED
        LOCK_DATA: 2
4 rows in set (0.00 sec)

Here is intuitive and can not wait for the lock to view the relationship, we can use the view to see sys.innodb_lock_waits

root@localhost : (none) 09:44:52> select * from sys.innodb_lock_waits\G;
*************************** 1. row ***************************
            wait_started: 2018-01-14 21:51:59
                wait_age: 00:00:11 <-- 等待时间
          wait_age_secs: 11
            locked_table: `xiaoboluo`.`t_luoxiaobo` <-- 表信息
    locked_table_schema: xiaoboluo
      locked_table_name: t_luoxiaobo
  locked_table_partition: NULL
locked_table_subpartition: NULL
            locked_index: PRIMARY <-- 主键字段
            locked_type: RECORD <-- 行锁
          waiting_trx_id: 55566
    waiting_trx_started: 2018-01-14 21:51:59
        waiting_trx_age: 00:00:11
waiting_trx_rows_locked: 1
waiting_trx_rows_modified: 0
            waiting_pid: 8 <-- 被阻塞的线程id
          waiting_query: update t_luoxiaobo set datet_time=now() where id=2  <-- 被阻塞者正执行的sql语句
        waiting_lock_id: 55566:2:4:2
      waiting_lock_mode: X <-- 锁模式
        blocking_trx_id: 55562
            blocking_pid: 7  <-- 阻塞的线程id
          blocking_query: NULL <-- 阻塞者正执行的sql语句(已经执行完了,但没提交)
        blocking_lock_id: 55562:2:4:2
      blocking_lock_mode: X
    blocking_trx_started: 2018-01-14 21:34:44
        blocking_trx_age: 00:17:26 <-- 持锁时间
blocking_trx_rows_locked: 1
blocking_trx_rows_modified: 1
sql_kill_blocking_query: KILL QUERY 7
sql_kill_blocking_connection: KILL 7  <-- kill阻塞者的语句
1 row in set (0.02 sec)

MySQL 5.7 version can also be used sys.innodb_lock_waits view of the query, but in 8.0, view the different tables join query (the information_schema.innodb_locks and information_schema.innodb_lock_waits table used to replace the previous version in order performance_schema.data_locks and performance_schema.data_lock_waits table ).

5.6 and previous versions is not sys default library, you can use the following statement instead:

SELECT r.trx_wait_started AS wait_started,
  TIMEDIFF(NOW(), r.trx_wait_started) AS wait_age,
  TIMESTAMPDIFF(SECOND, r.trx_wait_started, NOW()) AS wait_age_secs,
  rl.lock_table AS locked_table,
  rl.lock_index AS locked_index,
  rl.lock_type AS locked_type,
  r.trx_id AS waiting_trx_id,
  r.trx_started as waiting_trx_started,
  TIMEDIFF(NOW(), r.trx_started) AS waiting_trx_age,
  r.trx_rows_locked AS waiting_trx_rows_locked,
  r.trx_rows_modified AS waiting_trx_rows_modified,
  r.trx_mysql_thread_id AS waiting_pid,
  sys.format_statement(r.trx_query) AS waiting_query,
  rl.lock_id AS waiting_lock_id,
  rl.lock_mode AS waiting_lock_mode,
  b.trx_id AS blocking_trx_id,
  b.trx_mysql_thread_id AS blocking_pid,
  sys.format_statement(b.trx_query) AS blocking_query,
  bl.lock_id AS blocking_lock_id,
  bl.lock_mode AS blocking_lock_mode,
  b.trx_started AS blocking_trx_started,
  TIMEDIFF(NOW(), b.trx_started) AS blocking_trx_age,
  b.trx_rows_locked AS blocking_trx_rows_locked,
  b.trx_rows_modified AS blocking_trx_rows_modified,
  CONCAT('KILL QUERY ', b.trx_mysql_thread_id) AS sql_kill_blocking_query,
  CONCAT('KILL ', b.trx_mysql_thread_id) AS sql_kill_blocking_connection
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
  INNER JOIN information_schema.innodb_locks bl ON bl.lock_id = w.blocking_lock_id
  INNER JOIN information_schema.innodb_locks rl ON rl.lock_id = w.requested_lock_id
ORDER BY r.trx_wait_started;

Reference: "MySQL Performance Tuning pyramid law"

Published 295 original articles · won praise 35 · views 80000 +

Guess you like

Origin blog.csdn.net/Hehuyi_In/article/details/99351846