0%

innodb锁

前言:初涉innodb锁,把目前学到的先总结一下

锁分类

image-20200101003605172

隔离级别

image-20200101003614708

越往下,隔离级别越高,问题越少,同时并发度也越低。隔离级别和并发度成反比的。

  • 脏读:事务A读取了事务B未提交的数据
  • 不可重复读:对于一条记录,事务A两次读取的数据变了
  • 幻读:事务A按照相同的查询条件,读取到了新增的数据

image-20200101003628116

当前读与快照读

当前读:即加锁读,读取记录的最新版本,会加锁保证其他并发事务不能修改当前记录,直至获取锁的事务释放锁;

1
2
3
4
5
select * from table where ? lock in share mode;
select * from table where ? for update;
insert into table values (…);
update table set ? where ?;
delete from table where ?;

注:当Update SQL被发给MySQL后,MySQL Server会根据where条件,读取第一条满足条件的记录,然后InnoDB引擎会将第一条记录返回,并加锁,待MySQL Server收到这条加锁的记录之后,会再发起一个Update请求,更新这条记录。一条记录操作完成,再读取下一条记录,直至没有满足条件的记录为止。因此,Update操作内部,就包含了当前读。同理,Delete操作也一样。Insert操作会稍微有些不同,简单来说,就是Insert操作可能会触发Unique Key的冲突检查,也会进行一个当前读。

快照读:即不加锁读,读取记录的快照版本而非最新版本,通过MVCC实现;

InnoDB默认的RR事务隔离级别下,不显式加『lock in share mode』与『for update』的『select』操作都属于快照读,保证事务执行过程中只有第一次读之前提交的修改和自己的修改可见,其他的均不可见;

MVCC

MVCC『多版本并发控制』(Multi-Version Concurrency Control ),与之对应的是『基于锁的并发控制』(LBCC);

MVCC的最大好处:读不加任何锁,读写不冲突,对于读操作多于写操作的应用,极大的增加了系统的并发性能;

InnoDB默认的RR事务隔离级别下,不显式加『lock in share mode』与『for update』的『select』操作都属于快照读,使用MVCC,保证事务执行过程中只有第一次读之前提交的修改和自己的修改可见,其他的均不可见;

乐观锁

乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁

即先修改,保存时判断是够被更新过(CAS),应用级别,适用于写比较少的情况,冲突较少

阿里巴巴java开发手册:如果线程访问冲突小于20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不小于3次

悲观锁

悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁

即先获取锁,再操作修改,数据库级别,适用于冲突写比较多的情况,冲突较多

共享锁与排他锁

S锁,也叫做读锁、共享锁,对应于我们常用的 select * from users where id=1 lock in share mode

X锁,也叫做写锁、排它锁、独占锁、互斥锁,对应对于select * from users where id=1 for update

image-20200101003726540

这里要提到的一点是,S锁 和 X锁是可以是表锁,也可以是行锁

image-20200101003736543

行锁与表锁

表级锁:给表加锁,开销小,加锁快;不会出现死锁;锁定粒度大,发生锁冲突的概率最高,并发度最低。

行级锁:给行加锁,开销大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。

行锁是加在索引上的。

行锁有三种

image-20200101003743806

意向锁

事务在请求某一行的S锁和X锁前,需要先获得对应表的IS、IX锁。

IS: 意向共享锁 IX: 意向排他锁

意向锁产生的主要目的是为了处理行锁和表锁之间的冲突,用于表明“某个事务正在某一行上持有了锁,或者准备去持有锁”。比如,表中的某一行上加了X锁,就不能对这张表加X锁。

image-20200101003757783

记录锁(Record Locks)

记录锁定是对索引记录的锁定。例如, SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 可以防止从插入,更新或删除行,其中的值的任何其它交易t.c1是 10。

记录锁定始终锁定索引记录,即使没有定义索引的表也是如此。对于这种情况,请 InnoDB创建一个隐藏的聚集索引,并将该索引用于记录锁定

  • 锁是非主键索引,会在索引记录上加锁后,再去主键索引上加锁
  • 表上没有索引,会在隐藏的主键索引上加锁
  • 如果要锁的列没有索引,进行全表记录加锁

间隙锁

间隙锁定是对索引记录之间的间隙的锁定,或者是对第一个或最后一个索引记录之前的间隙的锁定。例如,SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE;阻止其他事务将value 15插入column中t.c1,无论该列 中是否已经存在该值,因为范围内所有现有值之间的间隙都被锁定。

间隙可能跨越单个索引值,多个索引值,甚至为空。

间隙锁是性能和并发性之间权衡的一部分,并且在某些事务隔离级别而非其他级别中使用。

RR级别,左右都是开区间

间隙锁用于防止幻读

  1. 防止间隙内有新数据被插入
  2. 防止已存在的数据,更新成间隙内的数据(例如防止numer=3的记录通过update变成number=5)

Next-key

记录锁(record key) + 间隙锁(gap key)

-------------本文结束感谢您的阅读-------------