锁篇
# 乐观锁&悲观锁
悲观锁:锁机制实现,是一种独占锁,一个事务开启悲观锁后,数据库会锁着正在查询的记录或表,其它事务查询会处于阻塞状态,尤其堆与长事务,会造成数据库的性能开销;
乐观锁:不借助锁机制,在提交数据时,才会对数据的冲突进行检测,典型实现CAS
,更新失败的概率高,占用CPU,会出现ABA问题;
# Mysql锁分类
全局锁
整个数据库处于只读状态,所有数据增删改&表结构更改操作都被阻塞,主要用于全库逻辑备份;
表级锁
表锁
//表级别的共享锁,也就是读锁; lock tables t_student read; //表级别的独占锁,也就是写锁; lock tables t_stuent write;
1
2
3
4
5表锁粒度太大,影响并发性能;
元数据锁
保证当前用户对表执行CRUD,防止其他线程对这个表结构更改;
意向锁
作用:为了快速判断表里是否有记录被加锁,如果没有「意向锁」,那么加「独占表锁」时,就需要遍历表里所有记录,查看是否有记录存在独占锁,这样效率会很慢;
- 在使用 InnoDB 引擎的表里对某些记录加上「共享锁」之前,需要先在表级别加上一个「意向共享锁」;
- 在使用 InnoDB 引擎的表里对某些纪录加上「独占锁」之前,需要先在表级别加上一个「意向独占锁」;
意向共享锁和意向独占锁是表级锁,不会和行级锁发生冲突;
行级锁
//对读取的记录加共享锁 select ... lock in share mode; //对读取的记录加独占锁 select ... for update; //对操作的记录加独占锁(X型锁) update table .... where id = 1; //对操作的记录加独占锁(X型锁) delete from table where id = 1;
1
2
3
4
5
6
7
8
9
10
11上面这两条语句必须在一个事务中,因为当事务提交了,锁就会被释放;
Record Lock,记录锁,也就是仅仅把一条记录锁上;
Gap Lock,间隙锁,锁定一个范围,但是不包含记录本身,防止数据插入,是可以共存的,一个事务获取间隙锁不会阻止另一个事务获取同一个间隙范围的间隙锁;
Next-Key Lock:Record Lock + Gap Lock 的组合,锁定一个范围,并且锁定记录本身。
插入意向锁:一个事务插入一条记录时,需要判断插入位置是否被其他事务加了间隙锁,如果有插入操作阻塞,生成一个插入意向锁;
它是一种特殊的间隙锁,只用于并发插入操作,与间隙锁冲突,两个事务同一个时间内不能一个拥有间隙锁,一个拥有该间隙区间内的插入意向锁;
# update语句每家索引导致锁全表?
一般情况,事务 A 的 update 语句中 where 若是等值查询,并且 id 是唯一索引,只会对 id = 1 这条记录加锁,事务 B 其它记录的更新操作并不会阻塞;
但是,在 update 语句的 where 条件没有使用索引,就会全表扫描,于是就会对所有记录加上 next-key 锁(记录锁 + 间隙锁),相当于把整个表锁住;
# 数据库死锁
Innodb引擎为解决可重复读隔离级别下的幻读问题,引入了临键锁;
死锁案例:
记录的最后一个记录为1006;
两个事务都执行了select ... for update
加了临键锁,锁的范围是(1006,+INF),临键锁优化为间隙锁,间隙锁与间隙锁之间不冲突;
但insert语句,会在间隙锁上获取插入意向锁,插入意向锁与间隙锁是冲突的,事务A的插入意向锁等待事务B的间隙锁释放;
如何避免?
设置事务等待锁的超时时间;
# Mysql如何加行级锁?
加锁的对象是索引,加锁的基本单位 临键锁;
# 唯一索引等值查询
- 记录存在,索引树上定位到这条记录,临键锁 退化为 记录锁;
- 记录不存在,索引树上定位到第一条 > 该查询记录的记录后,临键锁 退化为 间隙锁;
# 非唯一索引等值查询
非唯一索引等值查询时,存在两个索引,一个是主键索引,另一个是非唯一索引,加锁时,同时会对两个索引都加锁;
对于主键索引加锁,只有满足查询条件的记录才会对它们的主键加锁;
记录存在,由于不是唯一索引,可能存在索引值相同的记录,扫描到不符合条件的非唯一索引的记录就停止扫描,在扫描过程中,对扫描到的非唯一索引加 临键锁,对第一个不符合条件的非唯一索引记录,临键锁 退化为 间隙锁;
同时在符合查询条件的记录的索引上加记录锁;
记录不存在,扫描到第一个不符合条件的非唯一索引记录,临键锁 退化为 间隙锁;
不存在满足查询条件的记录,不会对主键索引加锁;
# 唯一索引范围查询
会对每一个扫描到的索引加 临键锁;
- >= 的范围查询,因存在等值查询,如果等值查询的记录存在表中,临键锁退化为记录锁;
- < 或 <= 的范围查询
- 条件值不存在表中,扫描到终止范围查询的记录时,该记录的索引的临键锁 退化为 间隙锁;其它扫描到的记录,还是临键锁;
- 条件值存在表中,< 范围查询 (同上),<= 返回查询 没有锁退化;
# 非唯一索引范围查询
无锁退化,全是临键锁;