InnoDB存储引擎——锁

Record Lock 行锁

单条索引记录上的锁,对索引进行加锁,而不是记录本身。如果没有索引,则对隐藏的聚集索引进行加锁。如果 sql 没有走索引,那么就会对每个聚集索引进行加锁

InnoDB 实现了标准的行级锁:

  • 共享锁(S):允许事务读一行数据
  • 排他锁(X):允许事务更新或删除一行数据

共享锁和排他锁的兼容性如下:

共享锁(S)排他锁(X)
共享锁(S)x
排他锁(X)xx

X 锁与任何锁都不兼容,S 锁只与 S 锁兼容。S 锁和 X 锁都是行锁,兼容是指对同一记录(row)锁的兼容性情况。
(1)事务 A 获取了行 R 上的共享锁,事务 B 可以获取行 R 上的共享锁,如果想要获取排他锁,则必须等事务 A 释放共享锁后才能获取锁
(2)事务 A 获取了行 R 上的排他锁,事务 B 无法获取行 R 上的任何锁,必须等事务 A 释放排他锁后才能去获取锁

意向锁

InnoDB 支持多粒度的锁,允许事务在行级别和表级别的锁同时存在,为了支持在不同粒度上进行加锁操作,InnoDB 支持一种额外的锁方式,称为意向锁,意向锁意味着事务希望在更细粒度上进行加锁。InnoDB 中意向锁即为表级锁。

  • 意向共享锁(IS):事务想要获得一张表中某几行的共享锁
  • 意向排他锁(IX):事务想要获得一张表中某几行的排他锁

表级意向锁和行级锁的兼容性如下:

ISIXSX
ISx
IXxx
Sxx
Xxxxx

InnoDB 意向锁不会阻塞行级锁。

Gap Lock 间隙锁

对索引记录中的间隙进行加锁,不包括记录本身

作用是为了防止多个事务将记录插入到同一范围内,导致幻读问题的产生。

关闭 Gap Lock 方式:

  • 事务隔离级别设置为 READ COMMITTED
  • 将参数 innodb_locks_unsafe_for_binlog 设置为1

Next-Key Lock

Gap Lock + Record Lock,锁定一个范围,并且锁定记录本身

例如,一个索引有10,11,13,20这四个值,那么该索引可能被 Next-Key Locking的区间为:
(-∞,10],(10,11],(11,13],(13,20],(20,+∞)

当查询的索引含有唯一属性时,InnoDB 存储引擎会对Next-Key Lock进行优化,降级为 Record Lock,仅锁住索引本身,而不是范围

辅助索引:对于辅助索引,InnoDB存储引擎还会对辅助索引下一个键值加上 gap lock

示例演示

数据准备

有如下数据的一张表,a设置为了主键,b设置了辅助索引

abc
111
444
888
151515

范围查询

  • 主键索引,唯一索引,辅助索引
    执行 a < 4 的查询,锁住 a 字段区间为 (-∞,4] 的数据,包含 4 这个索引记录。
# 事务A,查询 a < 4 的数据
select * from t where a<4 for update;
# 事务B,插入一条数据,被阻塞
insert into t values(-1,-1,-1);
# 事务B,插入一条数据,被阻塞
insert into t values(2,2,2);
# 事务B,更新主键值a=4的数据,被阻塞
update t set c=5 where a=4;
# 事务B,插入 a > 4的数据,成功
insert into t values(5,5,5);

等值查询

  • 主键索引,唯一索引
    执行 a = 4 的查询,锁住索引=4的记录
# 事务A,查询 a = 4 的数据
select * from t where a=4 for update;
# 事务B,插入一条数据,成功
insert into t values(2,2,2);
# 事务B,插入一条数据,成功
insert into t values(5,5,5);
# 事务B,更新主键值a=4的数据,被阻塞
update t set c=5 where a=4;
  • 辅助索引
    执行 b = 4 的查询,锁住b字段区间(1,4) 、4、 (4,8) 的数据,也就是 (1,8)区间的数据
# 事务A,查询 b = 4 的数据
select * from t where b=4 for update;
# 事务B,更新字段b=1的数据,成功
update t set c=5 where b=1
# 事务B,更新字段b=8的数据,成功
update t set c=5 where b=8;
# 事务B,插入一条数据,成功
insert into t values(-1,-1,-1);
# 事务B,插入一条数据,阻塞
insert into t values(2,2,2);
# 事务B,插入一条数据,阻塞
insert into t values(5,5,5);

死锁

两个或两个以上的事务在执行的过程中,因争夺资源而造成的一种互相等待的现象。

解决死锁办法:

  • 超时回滚:当两个事务相互等待时,当一个等待时间超过设置的阈值,其中一个事务进行回滚,另一个等待的事务就能继续进行。参数 innodb_lock_wait_timeout设置超时时间
  • 采用 wait-for graph(等待图)的方式进行死锁检测,InnoDB也使用这种方式进行主动检测,采用深度优先的算法实现,发现图存在回路,则判定存在死锁,回滚 undo 量较小的事务。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值