MySQL之锁

死锁

死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环。当事务试图以不同的顺序锁定资源时,就可能产生死锁。
锁的行为和顺序和存储引擎相关。以同样的顺序执行语句,有些存储引擎会产生死锁有些不会——死锁有双重原因:真正的数据冲突;存储引擎的实现方式。

死锁演示
创建表test 的SQL语句

CREATE TABLE `test` (
  `a` int NOT NULL,
  `b` int DEFAULT NULL,
  `c` int DEFAULT NULL,
  `d` varchar(45) DEFAULT NULL,
  PRIMARY KEY (`a`),
  KEY `idx_test_b` (`b`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
会话A会话B
begin;begin;
select * from test;
在这里插入图片描述
select * from test where a=4 for update;select * from test where a=7 for update;
select * from test where a=7 for update;–阻塞select * from test where a=4 for update;–阻塞
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

共享锁与排他锁

InnoDB行锁类型
共享锁(S):允许事务去读取一行数据。
排他锁(X):允许事务删除或更新一行数据。
如果一个事务T1已经获得行r的共享锁,那么另一个事务T2可以立即获得行r的共享锁,因为读取并没有改变行r的数据,称这种情况为锁兼容。但若有其他事务T3想获得行r的排他锁,则其必须等待事务T1,T2释放行r上的共享锁----这种情况称为锁不兼容。

XS
X不兼容不兼容
S不兼容兼容

加锁方式
共享锁(S):select * from table_name where id=1 lock in share mode;
排他锁(X):select * from table_name where id=1 for update;

for update是在数据库中上锁用的,可以为数据库中的行上一个排它锁。

InnoDB行锁实现方式
InnoDB行锁是通过给索引项加锁来实现的,如果没有索引,InnoDB将通过隐藏的聚簇索引来对记录加锁。

for update的使用场景
如果遇到存在高并发并且对于数据的准确性很有要求的场景,是需要了解和使用for update的。
比如涉及到金钱、库存等。一般这些操作都是很长一串并且是开启事务的。如果库存刚开始读的时候是1,而立马另一个进程进行了update将库存更新为0了,而事务还没有结束,会将错的数据一直执行下去,就会有问题。所以需要for upate 进行数据加锁防止高并发时候数据出错。
记住一个原则:一锁二判三更新

共享锁演示

会话A会话B
begin;begin;
select * from test;select * from test;
在这里插入图片描述
在这里插入图片描述
select * from test where a=1 lock in share mode;
在这里插入图片描述
select * from test where a =1;–非阻塞
在这里插入图片描述
update test set b=10 where a=1;–阻塞
在这里插入图片描述
commit;
在这里插入图片描述

排他锁演示

会话A会话B
begin;begin;
select * from test;
在这里插入图片描述
select * from test;
在这里插入图片描述
select * from test where a=1 for update;
在这里插入图片描述
select * from test where a=1;–非阻塞
在这里插入图片描述
select * from test where a=1 lock in share mode;–阻塞
在这里插入图片描述
update test set b=20 where a=1;–阻塞
在这里插入图片描述
commit;
在这里插入图片描述

表锁

InnoDB只有在通过索引条件检索数据时使用行级锁,否则使用表锁

会话A会话B
begin;begin;
select * from test where d=4 for update;
select * from test where a=1 lock in share mode;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction
insert into test(a,b,c,d) values(12,12,12,12);
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

记录锁、间隙锁、临键锁

记录锁(Record Locks)
单个行记录上的锁,例如:

select * from test where a=1 for update;

间隙锁
锁定一个范围,但不包含记录本身。

Next-key Lock
间隙锁 + 记录锁,锁定一个范围,并且锁定记录本身。

会话A会话B
begin;begin;
在这里插入图片描述
select * from test where a=5 for update; --查询不存在的记录
Empty set (0.00 sec)insert into test values(5,5,5,5);–阻塞
在这里插入图片描述
insert into test values(6,6,6,6);–阻塞
insert into test values(8,8,8,8);–非阻塞
在这里插入图片描述

检索条件 a=5 ,向左取得最靠近的值4作为左区间,向右取得最靠近的7作为右区间,因此,session 1的间隙锁的范围(4,5] 和(5,7],所以 a=5 或者 a=6 插入失败,a=8 可以插入成功。

意向锁

  • 意向共享锁(IS Lock): 事务想要获得一张表中某几行的共享锁。
  • 意向排他锁(IX Lock): 事务想要获得一张表中某几行的排他锁。
    由于InnoDB存储引擎支持的是行级别的锁,因此意向锁其实不会阻塞除全表扫以外的任何请求。意向锁的作用就是:
    当一个事务在需要获取资源的锁定时,如果该资源已经被排他锁占用,则数据库会自动给该事务申请一个该表的意向锁。如果自己需要一个共享锁定,就申请一个意向共享锁。如果需要的是某行(或者某些行)的排他锁定,则申请一个意向排他锁。
ISIXSX
IS兼容兼容兼容不兼容
IX兼容兼容不兼容不兼容
S兼容不兼容兼容不兼容
X不兼容不兼容不兼容不兼容

锁定的资源是什么?

认识了mySQL中的各种锁,我们来看下,mySQl锁,到底锁住的是什么?我们猜想mySQL锁,锁住的资源是主键。看下面的SQL语句:

会话A会话B
begin;begin;
update test set a=1111 where a =1;update test set b=111 where b=1;–阻塞等待
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0

表test 有2个索引,一个是聚集索引(a),另一个是普通索引(b)。我们知道InnoDB的普通索引的叶子节点保存的是主键Id。所以普通索引的查找需要回表操作。如果这时主键被其他事务加锁,这当前会话则进入阻塞等待。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值