MySQL锁

  |   0 评论   |   0 浏览

行锁:

是通过给索引上的索引项加锁来实现的(where条件一定要有索引),只有通过索引条件检索数据的数据,InnoDB才使用行级锁,否则将使用表锁!

查看当前行锁状态

show status like 'innodb_row_lock%'

Innodb_row_lock_current_waits:   当前正在等待锁定的数量
Innodb_row_lock_time:            从系统启动到现在锁定总时间的长度
Innodb_row_lock_time_avg:        每次等待所花平均时间
Innodb_row_lock_time_max:        从系统启动到现在等待最长的一次所花的时间
Innodb_row_lock_waits:           系统启动后到现在总共等待的次数
使用索引加行锁,未锁定的行可以访问
session1: begin;
select * from mylock where id = 1 lock in share mode; --手动加id=1的行锁,使用索引
session2: update mylock set name = 'y' where id =2 ; -- 未锁定行可以修改
session2: update mylock set name = 'y' where id = 1; -- 锁定改行无法修改,阻塞
session1: commit ; -- 提交事务 或者 rollback 释放读锁
session2: update mylock set name ='y' where id =1; --修改成功
未使用索引行锁,行读锁升级为表锁
session1: begin;
select * from mylock where name= 'c' lock in share mode; --手动加id=1的行锁,使用索引
session2: update mylock set name = 'y' where id =2 ; -- 修改阻塞 未使用索引行,行锁升级为表锁
session1: commit ; -- 提交事务 或者 rollback 释放读锁
session2: update mylock set name ='y' where id =2; --修改成功
主键索引产生记录锁(行写锁)
session1: begin 
select * from mylock where id =1 for update; -- 手动加id=1的行写锁
session2: select * from mylock where id =2; -- 可以访问
session2: select * from mylock where id = 1; --可以读,不加锁
session2: select * from mylock where id =1 lock in share mode; -- 加读锁,被阻塞
session1: commit ;
session2: 执行成功

间隙锁 --- 防止幻读

  1. 防止插入间隙内的数据
  2. 防止已有数据更新为间隙内的数据

a. 非唯一索引的等值 会产生间隙锁

news表记录(id 是主键, number有索引 idx_num)

idnumber
12
34
65
85
105
1311

第二条记录 number = 4 存在的间隙为:
id的间隙: 和上一条记录间隙 1,2,3. 和下一条记录id间隙 3,4,5,6
number的间隙: 和上一条记录间隙2,3,4. 和下一条记录number间隙 4,5. 其中这个5属于下边缘
如果不是数字,则取ascii码值.

session1:
start transaction; -- 开启事物 , 相当于begin
update news set number=3 where number = 4;  --这条记录首先会在上表中第二条数据产生行锁,然后也会产生间隙锁
session 2: 

start transaction ; 

insert into news value(2,3);#(均在间隙内,阻塞) 

insert into news value(7,8);#(均在间隙外,成功) 

insert into news value(2,8);#(id在间隙内,number在间隙外,成功) 

insert into news value(4,8);#(id在间隙内,number在间隙外,成功) 

insert into news value(7,3);#(id在间隙外,number在间隙内,阻塞) 

insert into news value(7,2);# (id在间隙外,number为上边缘数据,阻塞) 

insert into news value(2,2);#(id在间隙内,number为上边缘数据,阻塞) 

insert into news value(7,5);#(id在间隙外,number为下边缘数据,成功) 

insert into news value(4,5);#(id在间隙内,number为下边缘数据,阻塞)

结论: 只要number (where后面的) 在间隙里(2,3,4), 不包含最后一个数(5) 则不管id是多少都会阻塞。

b. 主键范围 产生间隙锁

--主键索引范围
session 1: 

start transaction ; 

update news set number=3 where id>1 and id <6; 

session 2: 

start transaction ; 

insert into news value(2,3);#(均在间隙内,阻塞) 

insert into news value(7,8);#(均在间隙外,成功) 

insert into news value(2,8);#(id在间隙内,number在间隙外,阻塞) 

insert into news value(4,8);#(id在间隙内,number在间隙外,阻塞) 

insert into news value(7,3);#(id在间隙外,number在间隙内,成功) 

--id无边缘数据,因为主键不能重复

结论: 只要id (在where 后面的) 在间隙里(2,4,5), 则不管number是多少都会阻塞。

c. 非唯一索引无穷大

--无穷大
session 1: 

start transaction ;
update news set number=3 where number=13 ; 

session 2: 

start transaction ; 

insert into news value(11,5);#(执行成功) 

insert into news value(12,11);#(执行成功) 

insert into news value(14,11);#(阻塞) 

insert into news value(15,12);#(阻塞) 

检索条件number=13,向左取得最靠近的值11作为左区间,向右由于没有记录因此取得无穷大作为右区间,因 

此,session 1的间隙锁的范围(11,无穷大) 

结论:id和number同时满足 

注:非主键索引产生间隙锁,主键范围产生间隙锁

死锁

1.session1: begin;--开启事务未提交
--手动加行写锁 id=1 ,使用索引 

update mylock set name='m' where id=1; 

2. session2:begin;--开启事务未提交 

--手动加行写锁 id=2 ,使用索引 

update mylock set name='m' where id=2; 

3. session1: update mylock set name='nn' where id=2; -- 加写锁被阻塞 

4. session2:update mylock set name='nn' where id=1; -- 加写锁会死锁,不允许操作 

ERROR 1213 (40001): Deadlock found when trying to get lock; try restarting 

transaction

标题:MySQL锁
作者:码农路上
地址:https://wujingjian.club/articles/2021/04/01/1617275339170.html