1. 什么是锁

  • 用于共享资源的并发访问,如果加锁,就不允许其他人对其进行操作
  • InooDB支持行锁,MyISAM只支持表锁,在并发插入时,性能差
  • InooDB的行锁没有相关开销,所以可以设置很多,也不会出现锁升级

2. InooDB的锁

2.1 锁的类型

2.1.1 行级锁(存储引擎实现的)

  • 共享锁(S Lock):允许事务读一行数据
start transaction;
select * from test where id = 1 lock in share mode;
  • 排他锁(X Lock):允许事务更新或删除一行数据
start transaction;
select * from test where id = 1 for update;

只有两个数据都进行读的操作时,共享锁是兼容的,出现冲突就需要先等原来的锁释放

阻塞

  • 一个事务的锁需要等另外一个事务

2.1.2 意向锁

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

<mark>注意:</mark>

  • 因为InnoDB支持的是行级锁,意向锁不会阻塞全表扫描外的任何请求
  • 意向锁表示下一行想要申请的锁,即看看下一行他的是否有锁

2.1.3 死锁

  • A在等待B,B在等待A
  • 当B发现出现死锁时,会回滚B的事务,使A获得了数据

2.1.4 锁升级

  • 当扫描全表时(比如扫描的记录太多,优化器不决定走索引)加表锁
  • 将1000行锁升级为表锁(Service层实现)
  • 表锁升级为全局锁

2.1.5 Next-Key Locks 临键锁(间隙锁–GAP)

  • Next-Key Locks 是 MySQL 的 InnoDB 存储引擎的一种锁实现。
  • MVCC 不能解决幻影读问题,Next-Key Locks 就是为了解决这个问题而存在的。在可重复读(REPEATABLE READ)
  • 隔离级别下,使用 MVCC + Next-Key Locks 可以解决幻读问题
  • 不仅锁定一个记录上的索引,也锁定索引之间的间隙,锁定一段左开右闭的索引区间。。例如一个索引包含以下值:10, 11, 13, and 20,那么就需要锁定以下区间

(-∞, 10]
(10, 11]
(11, 13]
(13, 20]
(20, +∞)

  • 间隙锁产生在:临键锁只与非唯一索引列有关,在唯一索引列(包括主键列)上不存在临键锁。
  • 只有当锁住的全部数据都被修改时,会被阻塞

临键锁和间隙锁的区别

  • 间隙锁只是加到了间隙里,行记录不会加锁
  • 临键锁在行记录和间隙都会加锁

2.1.6 记录锁

  • 记录锁存在于包括主键索引在内的唯一索引中,锁定单条索引记录。
  • 需要注意的是:id 列必须为唯一索引列或主键列,否则加的锁就会变成临键锁。
  • 同时查询语句必须为精准匹配(=),不能为 >、<、like、或者等值没有命中一条记录等,否则也会退化成间隙锁 (左开右开)

2.1.7 行锁总结(重点)

主键索引

等值查询

  • 命中一条记录:命中记录的加记录锁
  • 没有命中记录:加间隙锁

范围查询

  • 命中一条或多条记录:加临键锁
  • 没有命中记录:加间隙锁

非主键索引

等值查询

  • 命中一条记录:命中记录的辅助索引和主键索引加记录锁,记录两边加间隙锁
  • 没有命中记录:加间隙锁(边界不会阻塞)

范围查询

  • 命中一条或多条记录:包含where区间加临键锁,命中的记录主键锁加记录锁,如命中一条记录a,则a两边都会加锁
  • 没有命中记录:只加临键锁而没有记录锁

2.2 一致性的非锁定读操作

  • 通过多版本控制,读取当前执行时间行的数据。
  • 如果此时行在执行删除或更改操作,不会等待锁的释放,而是读取一个快照数据
  • 是通过Undo的回滚实现的,读取的是历史数据,对不同的事务隔离级别,读取的数据不同
  • 是InnoDB的默认读取方式

3 锁的问题

3.1 丢失更新

  • 此时User的修改会被覆盖
  • 解决的方式是:在User1读数据的时候,加一个排他锁,这样User2查询数据的时候就需要等待User1更改完成

3.2 脏读

  • 脏页:在缓冲池中修改的数据,数据已经写入重做日志中,只是缓冲池中的数据和磁盘的数据不一致
  • 脏数据:在缓冲池进行更改的但是没有commit

脏读是一个事务会读取到另个事务未提交的数据

3.3 不可重复读

  • 即前面脏读的数据提交,造成多次读的数据不一致

3.4 幻影读

T1 读取某个范围的数据,T2 在这个范围内插入新的数据,T1 再次读取这个范围的数据,此时读取的结果和和第一次读取的结果不同

4. 锁粒度

  • 锁定的资源越少,系统并发程度越高
  • 但是加锁需要消耗资源,所以需要在锁开销和数据安全性之间寻求平衡

MySQL提供两种锁策略

  • 行锁:给一行数据加锁,所有其他对这行数据的操作都会阻塞
  • 表锁:给一个表加锁,所有其他对表的操作都会阻塞