mysql死锁和等待锁

一.msyql死锁

  • 死锁是相互争用资源过程中,都等待彼此释放资源,产生的现象。

死锁场景模拟

  • 当session1持有共享锁S,并请求排他锁X,同时session2持有排他锁,并请求共享锁S的时候会产生死锁。

1)模拟session1持有共享锁,session2持有排他锁

## 在session1中执行
start transaction;
Query OK, 0 rows affected (0.00 sec)

select * from aa where userid = 18 lock in share mode;
+----+--------+
| id | userid |
+----+--------+
|  7 |     18 |
+----+--------+
1 row in set (0.00 sec)

## 在session1未提交之前,在session2执行删除,请求排他锁
start transaction;
Query OK, 0 rows affected (0.00 sec)

## 执行删除的时候,delete会一直阻塞,直到超过innodb_lock_wait_timeout,返回错误信息ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
delete from aa where id=7;

## 为了实验观察,调整innodb_lock_wait_timeout
show variables like 'innodb_lock_wait_timeout';
+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| innodb_lock_wait_timeout | 3600  |
+--------------------------+-------+
1 row in set (0.00 sec)

2)观察执行线程执行情况

## session2一直在执行
select * from information_schema.processlist order by state desc;
+----+------+-----------+------+---------+------+-----------+------------------------------------------------------------------+
| ID | USER | HOST      | DB   | COMMAND | TIME | STATE     | INFO                                                             |
+----+------+-----------+------+---------+------+-----------+------------------------------------------------------------------+
|  4 | root | localhost | test | Query   |   49 | updating  | delete from aa where id=7                                        |
|  5 | root | localhost | NULL | Query   |    0 | executing | select * from information_schema.processlist order by state desc |
|  3 | root | localhost | test | Sleep   |  331 |           | NULL                                                             |
|  7 | root | localhost | NULL | Sleep   |  991 |           | NULL                                                             |
+----+------+-----------+------+---------+------+-----------+------------------------------------------------------------------+
4 rows in set (0.00 sec)

3)模拟session1再持有排他锁

## 在session1中再执行
delete from aa where id=7;
Query OK, 1 row affected (0.00 sec)

## session1执行成功的同时session2的delete操作返回死锁错误信息
ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting transaction

4)检查死锁情况
show engine innodb status\G;

MySQL thread id 2, OS thread handle 140247796619008, query id 141 localhost root updating
delete from aa where id=7
*** (2) HOLDS THE LOCK(S):
RECORD LOCKS space id 27 page no 3 n bits 80 index PRIMARY of table `test`.`aa` trx id 3336 lock mode S
Record lock, heap no 1 PHYSICAL RECORD: n_fields 1; compact format; info bits 0
 0: len 8; hex 73757072656d756d; asc supremum;;

*** (2) WAITING FOR THIS LOCK TO BE GRANTED:
RECORD LOCKS space id 27 page no 3 n bits 80 index PRIMARY of table `test`.`aa` trx id 3336 lock_mode X locks rec but not gap waiting
Record lock, heap no 8 PHYSICAL RECORD: n_fields 4; compact format; info bits 0
 0: len 4; hex 80000007; asc     ;;
 1: len 6; hex 000000000b04; asc       ;;
 2: len 7; hex a4000001180128; asc       (;;
 3: len 4; hex 80000012; asc     ;;

5)查看历史执行情况
select * from performance_schema.events_statements_history;

二.mysql等待锁

  • mysql8.0已经废弃掉innodb_lock_waits表
  • state状态是Waiting for table metadata lock,事务A等待事务B释放资源
## session1中执行
set autocommit=0;
Query OK, 0 rows affected (0.00 sec)

select * from aa where id >6;
+----+--------+
| id | userid |
+----+--------+
|  7 |     18 |
+----+--------+
1 row in set (0.00 sec)

## session2中执行修改表,会一直等待session1中事务提交
alter table aa add column addr varchar(100) not null default '';

## session3中查看线程信息,可以看到状态是Waiting
select * from information_schema.processlist order by state desc;
+----+------+-----------+------+---------+------+---------------------------------+------------------------------------------------------------------+
| ID | USER | HOST      | DB   | COMMAND | TIME | STATE                           | INFO                                                             |
+----+------+-----------+------+---------+------+---------------------------------+------------------------------------------------------------------+
|  3 | root | localhost | test | Query   |  236 | Waiting for table metadata lock | alter table aa add column addr varchar(100) not null default ''  |
|  4 | root | localhost | NULL | Query   |    0 | executing                       | select * from information_schema.processlist order by state desc |
|  2 | root | localhost | test | Sleep   |  130 |                                 | NULL                                                             |
|  5 | root | localhost | NULL | Sleep   | 4597 |                                 | NULL                                                             |
+----+------+-----------+------+---------+------+---------------------------------+------------------------------------------------------------------+

三.锁相关的表结构解析

  • mysql8.0已经废弃掉innodb_lock_waits和inndb_locks表,取而代之的是performance_schema库中新增的data_lock_waits,data_locks。

1)innodb_trx表

字段说明
trx_id事务id,只读事务和非锁事务不会创建id的
trx_state事务状态,running / lock wait / rolling back /committng
trx_started事务开始时间
trx_requested_lock_id事务当前正在等待锁的标识
trx_wait_started事务开始等待的时间
trx_weight事务权重,代表修改的行数和被事务锁住的行数
trx_mysql_thread_id事务线程id
trx_query事务正在执行的sql语句
trx_operation_state事务当前操作状态
trx_tables_in_use当前事务执行sql中使用的表的个数
trx_tables_locked当前执行sql的行锁数量
trx_lock_structs事务保留的锁数量
trx_lock_memory_bytes事务锁住的内存大小
trx_rows_locked事务锁住的记录数
trx_rows_modified事务更改的行数
trx_concurrency_tickets当前事务在被清掉之前可以多少工做,由 innodb_concurrency_tickets系统变量值指定
trx_isolation_level当前事务的隔离级别
trx_unique_checks是否打开唯一性检查的标识
trx_foreign_key_checks是否打开外键检查的标识
trx_last_foreign_key_error最后一次外键错误的信息
trx_adaptive_hash_latched自适应哈希索引是否被当前事务阻塞
trx_adaptive_hash_timeout是否为了自适应hash索引立即释放查询所,或者通过调用mysql函数保留它

2)innodb_lock_waits

字段说明
requesting_trx_id请求事务的id
requested_lock_id事务锁等待的锁定的id,可以关联innodb_locks获取更多信息
blocking_trx_id阻塞事务的id
blocking_lock_id某一事务的锁的id,该事务阻塞的了另一事务的运行

3)innodb_locks

字段说明
lock_id锁id
lock_trx_id持有锁的事务id
lock_mode锁模式
lock_type锁的类型。record:行锁;table:表锁
lock_table被锁定的或者包含的锁定记录的表的名称
lcok_index索引名称,所类型为行锁才会显示,否则为null
lock_space锁定行的表空间id,表锁为null
lock_page锁定行的页号,表锁为null
lock_rec锁定行数,表锁为null
lock_data锁定行主键,表锁为null

4)data_lock_waits表

字段说明
engine请求锁定的存储引擎
requesting_engine_lock_id存储引擎请求的锁的id
requesting_engine_transaction_id请求锁定的事务的存储引擎内部id
requesting_thread_id请求锁定的会话的线程id
requesting_event_id在请求锁定的会话中导致锁定的performance schema事件
requesting_object_instance_begin请求的锁在内存中的地址
blocking_engine_lock_id阻止锁的id,关联data_locks可以获取锁更多信息
blocking_engine_transaction_id拥有阻止锁的事务的存储引擎内部标识
blocking_thread_id拥有阻止锁的会话的线程id
blocking_event_id在持有该锁的会话中导致阻塞锁的performance schema事件
blocking_object_instance_begin阻塞锁在内存中的地址

5)data_locks表

字段说明
engine存储引擎
engine_lock_id存储引擎内部的锁id
engine_transaction_id等同innodb_tr中的trx_id
thread_id持有锁的线程id
event_id事件id
object_schema数据库名
object_name表名
partition_name分区名
subpartition_name子分区名
index_name索引名
object_instance_begin锁的内存空间起始地址
lock_type锁类型
lock_mode锁模式
lock_status锁状态,granted / waiting
lock_data锁的数据,当lock_type类型为record时才会有值

6)锁信息定位

## mysql5.7
## 显示谁阻塞和谁等待,以及等待多久的的查询
select r.trx_id as waiting_trx_id,r.trx_mysql_thread_id as waiting_thread,
timestampdiff(second,r.trx_wait_started,current_timestamp) as wait_time,
r.trx_query as waiting_query,l.lock_table as waiting_table_lock,
b.trx_id as blocking_trx_id,b.trx_mysql_thread_id as blocking_thread,substring(p.host,1,instr(p.host,':')-1) as blocking_host,
substring(p.host,instr(p.host,':')+1) as blocking_port,
if(p.command="Sleep",p.time,0) as idle_in_trx,b.trx_query as blocking_query
from information_schema.innodb_lock_waits as w
inner join information_schema.innodb_trx as b on b.trx_id = w.blocking_trx_id
inner join information_schema.innodb_trx as r on r.trx_id = w.requesting_trx_id
inner join information_schema.innodb_locks as l on w.requested_lock_id = l.lock_id
left join information_schema.processlist as p on p.id = b.trx_mysql_thread_id
order by wait_time;

## mysql5.7
## 线程在一个事务中空闲而正在遭受大量的锁操作,可以通过下面sql查询多少查询被哪些线程阻塞
select concat('thread ',b.trx_mysql_thread_id,' from ',p.host) as who_blocks,
if(p.command = "Sleep",p.time,0) as idle_in_trx,
max(timestampdiff(second,r.trx_wait_started,now())) as max_wait_time,
count(*) as num_waites
from information_schema.innodb_lock_waits as w
inner join information_schema.innodb_trx as b on b.trx_id = w.blocking_trx_id
inner join information_schema.innodb_trx as r on r.trx_id = w.requesting_trx_id
left join information_schema.processlist as p on p.id = b.trx_mysql_thread_id
group by who_blocks 
order by num_waiters;

## mysql8.0
select b.trx_query,c.trx_query,d.object_name,object_name,index_name
from performance_schema.data_lock_waits a
left join information_schema.innodb_trx b on a.requesting_engine_transaction_id = b.trx_id
left join information_schema.innodb_trx c on a.blocking_engine_transaction_id = c.trx_id
left join performance_schema.data_locks d on a.requesting_engine_lock_id =d.engine_lock_id;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值