0
点赞
收藏
分享

微信扫一扫

行锁分析5.7和8.0版本

在MySQL中,行锁并不是直接锁记录,而是锁索引,而索引又分为主键索引和非主键索引两种:如果一条SQL操作了主键索引,MySQL就会锁定这条主键索引;如果一条语句操作了非主键索引,MySQL会先锁定该非主键索引,再锁定相关的主键索引。在做更新删除操作时,MySQL不仅锁定WHERE条件扫描过的所有索引记录,而且会锁定相邻的键值,即next-key locking。


有事务锁(行锁)等待,使用以下语句定位锁:

MySQL 5.7版本:

select * from information_schema.innodb_locks;
select * from information_schema.innodb_lock_waits


MySQL 8.0版本:

select * from sys.innodb_lock_waits\G
或者
select * from performance_schema.data_lock_waits\G
select PROCESSLIST_ID,PROCESSLIST_USER,PROCESSLIST_DB from performance_schema.threads where thread_id=&BLOCKING_THREAD_ID;



--以下是MySQL 8.0版本测试

create table test2(id int,status varchar(10),time date);
create index idx_test2 on test2(status,time);
alter table test2 add primary key(id);


--插入130000条数据

drop procedure insertintotest2;
DELIMITER //
create procedure insertintotest2()
begin
declare i int default 0;
LOOP_LABLE:
loop
insert into test2 values(i,'a',sysdate()-20000);
set i=i+1;
if i>=130000 then
leave LOOP_LABLE;
end if;
end loop;
end;
//

CALL insertintotest2
//
DELIMITER ;



以下语句执行时,MySQL会使用idx_test2【status,time】索引,首先锁定相关的索引记录,因为idx_test2不是主键索引,因此MySQL还会锁定主键索引。

mysql> explain update test2 set status='aa',time=now() where status='a' and time<date_sub(now(), INTERVAL 10 minute);
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
|  1 | UPDATE      | test2 | NULL       | index | idx_test2     | PRIMARY | 4       | NULL | 2000 |   100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+------+------+----------+-------------+


以下语句和上一条语句几乎同时执行时,该语句会先锁定主键索引【ID】,由于需要更新status的值,所以还需要锁定idx_test2【status,time】的某些索引记录

mysql> explain update test2 set status='aaaa',time=now() where id=1;
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | UPDATE      | test2 | NULL       | range | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | Using where |
+----+-------------+-------+------------+-------+---------------+---------+---------+-------+------+----------+-------------+


关闭自动提交情况下,第一条语句锁定了idx_test2的记录,等待主键索引;第二条语句则锁定了主键索引记录,而等待idx_test2的记录,这种情况下锁就产生了



同时执行以下两个会话

会话1

update test2 set status='aa',time=now() where status='a' and time<date_sub(now(), INTERVAL 10 minute);


会话2

update test2 set status='aaaa',time=now() where id=1;



查看锁

mysql> select * from sys.innodb_lock_waits\G
*************************** 1. row ***************************
                wait_started: 2023-07-27 14:38:14
                    wait_age: 00:00:05              -->等待时间
               wait_age_secs: 5
                locked_table: `test`.`test2`
         locked_table_schema: test
         locked_table_schema: test                  -->表用户
           locked_table_name: test2                 -->表名称
      locked_table_partition: NULL
   locked_table_subpartition: NULL
                locked_index: PRIMARY               -->主键字段
                 locked_type: RECORD                -->行锁
              waiting_trx_id: 1246241
         waiting_trx_started: 2023-07-27 14:19:06
             waiting_trx_age: 00:00:08
     waiting_trx_rows_locked: 1
   waiting_trx_rows_modified: 0
                 waiting_pid: 9                     -->被阻塞的线程号
               waiting_query: update test2 set status='aaaa',time=now() where id=1
             waiting_lock_id: 139657526383016:11:6:3:139657558024224
           waiting_lock_mode: X,REC_NOT_GAP
             blocking_trx_id: 1246236
                blocking_pid: 8                     -->阻塞的线程号
              blocking_query: NULL                  -->阻塞者正在执行的SQL,这里是已经执行完了但是没提交
            blocking_lock_id: 139657526382120:11:6:3:139657558021152
          blocking_lock_mode: X
        blocking_trx_started: 2023-07-27 14:19:04   -->阻塞者开始执行时间
            blocking_trx_age: 00:19:15              -->持有锁时间
    blocking_trx_rows_locked: 130244                -->堵塞着的事务锁住了130244行记录
  blocking_trx_rows_modified: 130000                -->堵塞着的事务修改了130000行记录
     sql_kill_blocking_query: KILL QUERY 8
sql_kill_blocking_connection: KILL 8                -->KILL堵塞进程语句


或者


mysql> select * from performance_schema.data_lock_waits\G
*************************** 1. row ***************************
                          ENGINE: INNODB
       REQUESTING_ENGINE_LOCK_ID: 139657526383016:11:6:3:139657558024224
REQUESTING_ENGINE_TRANSACTION_ID: 1246241
            REQUESTING_THREAD_ID: 2
             REQUESTING_EVENT_ID: 43
REQUESTING_OBJECT_INSTANCE_BEGIN: 139657558024224
         BLOCKING_ENGINE_LOCK_ID: 139657526382120:11:6:3:139657558021152
  BLOCKING_ENGINE_TRANSACTION_ID: 1246236
              BLOCKING_THREAD_ID: 48     -->和performance_schema.threads表的thread_id关联可以找到PROCESSLIST_ID
               BLOCKING_EVENT_ID: 8201
  BLOCKING_OBJECT_INSTANCE_BEGIN: 139657558021152

select PROCESSLIST_ID,PROCESSLIST_USER,PROCESSLIST_DB from performance_schema.threads where thread_id=48;
+----------------+------------------+----------------+
| PROCESSLIST_ID | PROCESSLIST_USER | PROCESSLIST_DB |
+----------------+------------------+----------------+
|              8 | root             | test           |
+----------------+------------------+----------------+

 


通过事务ID,可以查看事务发起的账号和主机信息,KILL这个事务ID后锁就会释放


#查看下这个事务发起的账号和主机信息

select * from information_schema.processlist where id=8;
+----+------+-----------+------+---------+-------+-------+------+----------+-----------+---------------+
| ID | USER | HOST      | DB   | COMMAND | TIME  | STATE | INFO | TIME_MS  | ROWS_SENT | ROWS_EXAMINED |
+----+------+-----------+------+---------+-------+-------+------+----------+-----------+---------------+
|  8 | root | localhost | test | Sleep   | 10881 |       | NULL | 10881243 |         0 |        130000 |
+----+------+-----------+------+---------+-------+-------+------+----------+-----------+---------------+


#KILL未提交的事务线程ID

mysql> kill 8

 

--以下是MySQL 5.7版本测试

create table test1(id int,name int,cdate date,type varchar(10));

insert into test1 values(1,1,sysdate()-2,'a');
insert into test1 values(2,2,sysdate()-1,'b');
commit;
select * from test1;


会话1

update test1 set cdate=sysdate(),name=11 and id=11 and type='aa';


会话2

update test1 set cdate=sysdate(),name=11 and id=1111 and type='aa';


会话1

update test1 set cdate=sysdate(),name=11 and id=1111 and type='aa';


结果创建的索引的语句卡住

create index IDX_TEST_ID on test(id);


1、查看当前运行的所有事务

mysql> select * from information_schema.innodb_trx\G;
*************************** 1. row ***************************
trx_id: 4904
trx_state: RUNNING         -->事务状态
trx_started: 2021-11-2023:38:46
trx_requested_lock_id: NULL
trx_wait_started: NULL
trx_weight: 1
trx_mysql_thread_id: 8
trx_query: NULL
trx_operation_state: NULL
trx_tables_in_use: 0
trx_tables_locked: 1
trx_lock_structs: 1
trx_lock_memory_bytes: 1136
trx_rows_locked: 1        -->事务锁住的行数
trx_rows_modified: 0
trx_concurrency_tickets: 0
trx_isolation_level: REPEATABLEREAD
trx_unique_checks: 1
trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
trx_adaptive_hash_latched: 0
trx_adaptive_hash_timeout: 0
trx_is_read_only: 0
trx_autocommit_non_locking: 0


2、查看当前出现的锁

mysql> select * from information_schema.innodb_locks;
Empty set, 1 warning (0.00 sec)


3、如果有锁,查看锁等待的对应关系

mysql> select * from information_schema.innodb_lock_waits\G;
Empty set, 1 warning (0.00 sec)


4、查看锁情况

mysql> show status like 'innodb%lock%';
+-------------------------------+--------+
| Variable_name                 | Value  |
+-------------------------------+--------+
| Innodb_row_lock_current_waits | 0      |  --当前等待锁的数量
| Innodb_row_lock_time          | 102068 |  --系统启动到现在,锁定的总时间长度
| Innodb_row_lock_time_avg      | 51034  |  --每次平均锁定的时间
| Innodb_row_lock_time_max      | 51056  |  --最长一次锁定时间
| Innodb_row_lock_waits         | 2      |  --系统启动到现在总共锁定的次数
+-------------------------------+--------+


5、查询是否锁表:

mysql> show open tables where in_use>0;
+----------+-------+--------+-------------+
| Database | Table | In_use | Name_locked |
+----------+-------+--------+-------------+
| test     | test1 |      1 |           0 |
+----------+-------+--------+-------------+


6、查看锁表的进程

mysql> show processlist\G;
*************************** 1. row ***************************
     Id: 6
   User: root
   Host: localhost:46556
     db: test
Command: Query         -->该线程正在执行一个查询
   Time: 792
  State: Waiting for table metadata lock
   Info: create index IDX_TEST1_ID on test1(id)   -->这个语句就是卡住的创建索引语句
*************************** 2. row ***************************
     Id: 8
   User: root
   Host: localhost:46561
     db: test
Command: Sleep         -->该线程正在等待客户端向它发送执行语句
   Time: 1319
  State: 
   Info: NULL


7、尝试killId: 8的sleep的会话

kill 8;


8、创建索引的会话立刻结束

mysql> create index IDX_TEST1_ID on test1(id);
Query OK, 0 rows affected (19 min 56.63 sec)
Records: 0  Duplicates: 0  Warnings: 0

举报

相关推荐

0 条评论