🥁 锁
2022年6月20日
- db
 
🥁 锁
1. 锁的拥有者
1) 读锁 | 共享锁
select ... lock in share mode | for share[MySQL 8.0 新增]- 可以保证自己读到的是最新数据,只有没有其他事务竞争时,才可以修改数据
 
2) 写锁 | 排它锁
select ... for update- 若是查询的是索引,那么会加行锁
 - 若是查询的是普通字段,那么会加表锁
 
- 可以保证自己读到的是最新数据,并且该数据只允许自己修改数据,其他事务想要读取和修改,则被阻塞
 - 只有拥有该锁的事务可以读取和修改,其他事务都不可以读取和修改,并且同一时间只能有一个事务加写锁
 
2. 锁的粒度
1) 表级锁
- 共享锁 & 排它锁
- 表共享锁
lock tables chap readunlock tables释放表锁
 - 表排它锁
lock tables chap updateunlock tables释放表锁
 
 - 表共享锁
 - 意向锁
- 兼容性: 
- 意向锁之间是不会产生冲突的,也不和 AUTO_INC 表锁冲突
 - 只会阻塞 表级读锁或表级写锁
 - 意向锁也 不会和行锁冲突,行锁只会和行锁冲突
 
 - 意向共享锁
- 事务有意向对某些行加共享锁
 
 - 意向排它锁
- 事务有意向对某些行加排它锁
 
 - 作用: 
- 解决 表级锁 & 行级锁 共存的问题
 若没有意向锁,则我们添加 表锁 时,需要遍历表中所有行来判断有没有行锁,效率低- 若是存在意向锁,则我们添加 表锁 时,可以直接一次判断表中是否有数据行被锁定
 
 - 举例: 
- 事务 A 申请 写行锁 之前,数据库会自动给 事务 A 申请表的 意向排他锁
 - 当 事务 B 申请表的互斥锁时,就会失败
 
 
 - 兼容性: 
 
2) 页锁
3) 行锁
- 在 
RC隔离级别不会加任何锁 
// id 为主键,只需要在 id == 49 这个主键索引上加 写锁
update user set age = 10 where id = 49;
// name 为普通索引,首先在 name == 'Tom' 这个索引上加 写锁;然后回表,在 id == 49 这个主键索引上加 写锁
update user set age = 10 where name = 'Tom';
① 记录锁 [Record Lock]
- 锁定一个记录上的索引,而不是记录本身
 - 如果表没有设置索引,InnoDB 会自动在主键上创建隐藏的聚簇索引,因此 Record Locks 依然可以使用
 - 当 SQL 语句无法使用索引时,会进行全表扫描,这个时候 MySQL 会给整张表的所有数据行加记录锁, 
- 再由 MySQL Server 层进行过滤
 - 但是,在 MySQL Server 层进行过滤的时候,如果发现不满足 WHERE 条件,会释放对应记录的锁。
 - 这样做,保证了最后只会持有满足条件记录上的锁,但是每条记录的加锁操作还是不能省略的。
 - 因此,更新操作必须要根据索引进行操作,没有索引时,不仅会消耗大量的锁资源,增加数据库的开销,还会极大的降低了数据库的并发性能
 
 
② 间隙锁 [Gap Lock]
- 锁定索引之间的间隙,但是不包含索引本身
 - 为开区间
 - 间隙锁可以防止其他事务在这个范围内插入或修改记录,保证两次读取这个范围内的记录不会变,可以 防止幻读
 - 间隙锁和间隙锁之间是互不冲突的,间隙锁唯一的作用就是 防止其他事务的 insert
 
③ 临键锁 [Next-Key Lock]
- 不仅锁定一个记录上的索引,也锁定索引之间的间隙
 - 为 左开右闭 ( left , right ] 区间
 
④ 插入意向锁
- 插入意向锁 是 间隙锁 的一种,专门针对 
insert操作, - 官方意思是说多个事务在同一个索引同一个范围区间插入记录时候,如果插入位置不冲突,不会彼此阻塞 
id = 30和id = 49之间如果有两个事务要同时分别插入id = 32和id = 33是没问题的,- 虽然两个事务都会在 
id = 30和id = 50之间加上插入意向锁,但是不会冲突 
 - 若是在 
RC隔离级别,此时有个 update 事务还未提交,然后有个 insert 操作,此时 insert 操作不阻塞,不用等待 update 事务提交 - 若是在 
RR隔离级别,此时有个 update 事务还未提交,然后有个 insert 操作,此时 insert 操作阻塞,需要等待 update 事务提交- 从而保证了 RR 隔离级别下,通过 临键锁 实现了 串行化,保证在整个事务执行过程中,无论何时查到的数据记录条数不会改变
 
 - 插入意向锁 只会和 间隙锁或 Next-key 锁 冲突, 
- 间隙锁唯一的作用就是防止其他事务插入记录造成幻读,正是由于在执行 
INSERT语句时需要加插入意向锁,而插入意向锁和间隙锁冲突,从而阻止了插入操作的执行 
 - 间隙锁唯一的作用就是防止其他事务插入记录造成幻读,正是由于在执行 
 - 如果一个事务加了 插入意向锁,不影响其他事务加任何锁;但若是一个事务加了 临键锁 | 间隙锁,那么当前事务加 插入意向锁 时,会阻塞 
- 这里有个顺序问题
 
 
临键锁具体使用 [MySQL 8.0.26]
原始数据:
| id [主键] | b [普通索引] | a [无索引] | 
|---|---|---|
| 0 | 0 | 0 | 
| 4 | 4 | 4 | 
| 8 | 8 | 8 | 
| 16 | 16 | 16 | 
| 32 | 32 | 32 | 
- 唯一索引等值查询
 - 唯一索引范围查询
 - 非唯一索引等值查询 
- 当记录存在时,需要加 左临键锁 & 右间隙锁
 - 作用: 
- 之所以要把记录前后的间隙都锁住,仍然是为了防止幻读,
 - 因为是非唯一索引,所以 id = 49 可能会有多条记录,为了防止再插入一条 id = 49 的记录
 
 
 - 非唯一索引范围查询
 
3. 锁的态度
1) 悲观锁
- 优点: 
- 保证数据安全
 
 - 缺点: 
- 影响性能
 
 - 适用场景: 
- 写多读少的并发环境
 
 
2) 乐观锁
- 优点: 
- 避免加锁的开销,提高响应性能
 
 - 缺点: 
- 写竞争激烈情况下,多次 CAS 重试,CPU 损耗大
 
 - 适用场景: 
- 读多写少的并发环境
 
 - 如何实现: 
- 开发者自行实现
 - 可通过 添加
版本 | 时间戳实现 
 
4. 锁的方式
1) 显式锁
2) 隐式锁
5. 其他
1) 全局锁
2) 死锁
- 排查流程 
- 查看死锁日志 
show engine innodb status
 - 找出死锁 sql
 - 分析 sql 死锁情况
 - 模拟 死锁 案发
 - 分析 死锁 日志
 - 分析 死锁 结果
 
 - 查看死锁日志