其他分享
首页 > 其他分享> > 锁机制-innodb锁模式

锁机制-innodb锁模式

作者:互联网

目录

innodb的表级锁模式

innodb也支持表锁,只能手动加表锁,表锁命令同样适用于innodb,规则和myisam的规则一致

image

image

innodb的行级锁模式

InnoDB存储引擎默认使用行锁,支持表级锁。

默认情况下:不会自动加读锁,会自动加写锁。

支持事务

行锁解锁方式

开启autocommit后,手动 或 自动加的锁 在SQL执行完后 都会自动释放。
关闭autocommit后,手动 或 自动加的锁 都需要通过事务解锁。

关闭自动commit

--mysql默认自动commit; oracle默认不会自动commit ;
为了方便研究行锁,暂时将自动commit关闭,以后需要通过commit

准备数据

create table linelock
(
id int(5) primary key auto_increment,
name varchar(20)
)engine=innodb ;

insert into linelock(name) values ('1');
insert into linelock(name) values ('2');
insert into linelock(name) values ('3');
insert into linelock(name) values ('4');
insert into linelock(name) values ('5');

commit ;

写锁

操作同一条数据

两个会话update或delete同一条数据(即便是第一个会话刚插入还没有来得及提交的数据)

会话0:
delete from linelock where id = 3;

会话0:对其他行、其他表的操作

image

小结:
会话0对某条数据加锁后,该会话可以在行锁未释放的情况下对其他行、其他表继续操作,继续加锁。

会话1
update linelock set name='ax' where id = 3; -- 阻塞

select * from linelock where id = 3; -- innodb默认不加锁 执行成功

select * from linelock where id = 3 lock in share mode;
select * from linelock where id = 3 for update;

image

小结:
如果会话0对某条数据a进行DML操作,则其他会话对数据a进行操作必须等待会话0释放行锁(结束事务 commit/rollback)。(研究时:关闭了自动commit的情况下)

操作不同数据

会话1:写操作,不同的数据
update linelock set name='5x' where id = 5;  -- 执行成功

小结:
行锁,一次锁一行数据;因此如果操作的是不同数据,则不干扰。

间隙锁

行锁的一种特殊情况:间隙锁:值在范围内,但却不存在

update linelock set name = 'x' where id >1 and id<9 ; -- 此时linelock表中没有id=7的数据

在此where范围中,没有 id=7的数据,则id=7的数据成为间隙。Mysql会自动给间隙加锁->间隙锁。即本题会自动给id=7的数据加间隙锁(行锁)。

行锁:如果有where,则实际加锁的范围就是where后面的范围(不是实际的值)

死锁

事务1和事务2互相持有对方需要的锁而不释放,造成死锁的情况

事务1:update linelock set name='zs' where id=4;
事务2:update linelock set name='zs1' where id=5;

事务1:update linelock set name='ls' where id=5; -- 阻塞
事务2:update linelock set name='ls1' where id=4; -- 报错(死锁),事务2被干掉(前面执行的update5也被撤销),同时事务1的update5执行成功。

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

当事务1update4的时候,事务2update5,但是由于innodb是行锁,没有问题,可以执行

继续:事务1 update5 的时候,由于5被事务2锁着,没有释放,会被阻塞,等待事务2提交

然后事务2继续update4,报错:4被事务1锁着呢,同时事务1要操作5,但是5被事务2锁着呢,就遭成了死锁

对query语句加锁

innodb 默认不加读锁,如果需要更改 需要修改隔离级别,默认 隔离级别为 可重复读。

能否手动加锁?

读锁(共享锁、S锁):select ... lock in share mode;

写锁(排它锁、X锁):select ... for update;

手动加读锁

会话1:select * from linelock where id = 5 lock in share mode;
会话2:update linelock set name='zzz2' where id = 5; -- 阻塞
会话3:select * from linelock where id = 5 lock in share mode;

image

手动加写锁

会话1:select * from linelock where id=2 for update;
会话2:
select * from linelock where id = 2; -- 默认 不加锁
select * from linelock where id = 2 lock in share mode; -- 阻塞
select * from linelock where id = 2 for update; -- 阻塞

会话3:
update linelock set name = '222' where id = 2; -- 阻塞

image

行锁会转为表锁(什么情况下 行锁会锁全表)

1.如果 没有索引索引失效,则行锁会转为表锁

show index from linelock ;
alter table linelock add index idx_linelock_name(name);

-- 索引未失效
会话0:写操作
update linelock set name = 'ai' where name = '3';
会话1: 写操作,不同的数据
update linelock set name = 'diX' where name = '4' ;

-- 索引失效
会话0:写操作
update linelock set name = 'ai' where name = 3 ;
会话1:写操作,不同的数据
update linelock set name = 'aiX' where name = 4;
-- 可以发现数据被阻塞了(加锁)

原因: 如果索引类 发生了类型转换,则索引失效。因此此次操作,会从行锁转为表锁。

2.查询表中所有数据,或者 删除所有数据也都会锁全表

如果 select * from linelock 后面 不添加where条件,则转为表锁。

会话0:select * from linelock for update;

会话1:select * from linelock; -- 正常执行
delete from linelock where id =1; -- 阻塞

标签:name,update,模式,linelock,会话,innodb,机制,where,id
来源: https://www.cnblogs.com/jiyuchen1/p/16507886.html