读未提交下:
-
增:插入数据后,给新插入的数据上记录锁,防止其它事务更改这条记录
-
删:需要获取到要删除的记录的记录锁,保证删除的时候其它事务没在使用这些数据,并保证删除后其它事务无法对这些数据进行操作
-
改:给需要更改的数据上记录锁
-
查:普通select不上锁,直接读最新数据,不管是否提交,select…for update 或 select … lock in share mode会上记录锁,不锁间隙!
读已提交下:
-
增:插入数据后,给新插入的数据上记录锁,防止其它事务更改这条记录
-
删:需要获取到要删除的记录的记录锁,保证删除的时候其它事务没在使用这些数据,并保证删除后其它事务无法对这些数据进行操作
-
改:给需要更改的数据上记录锁
-
查:普通select不上锁,通过MVCC找到读取的数据,select…for update 或 select … lock in share mode会上记录锁,不锁间隙!
可重复读下:
-
增:使用插入****意向锁,进行插入,插入后给插入的数据上记录锁(隐式锁,其实这个记录锁不是这个时候上的)
-
删:使用临键锁,防止其它事务在删除的区间内插入数据
-
改:给需要更改的数据上临键锁
-
查:普通select不上锁,通过MVCC找到读取的数据,select…for update 或 select … lock in share mode会上临键锁,锁间隙!
串行化读下:
-
增:使用插入****意向锁,进行插入,插入后给插入的数据上记录锁(隐式锁,其实这个记录锁不是这个时候上的)
-
删:使用临键锁,防止其它事务在删除的区间内插入数据
-
改:给需要更改的数据上临键锁
-
查:普通查询自动变为select … lock in share mode,上临键锁,锁间隙!
上面上锁具有以下细节:
-
这些锁的释放是在事务提交后释放
-
删除操作和更新操作其实是差不多的,因为删除其实本质就是把行记录的一个逻辑删除标识位给改了,就相当于更新
-
MVCC只有在读已提交和可重复读中才有
-
间隙锁只有在可重复读和串行化读中才有
-
读已提交相对于读未提交只多了个MVCC,用于解决数据可见性问题,保证读到的是已提交的数据
-
可重复读相对于读已提交相当于把记录锁升级为临键锁,解决(大部分)幻读情况
-
串行化读相对于可重复读相当于普通读会上锁,解决全部幻读情况
更新操作怎么上锁?
-
例如有字段a,b,c,其中a,b字段上有索引
-
update table set a = 1 where a = 2; 该SQL会先去a索引获取锁,然后去聚集索引获取锁,全部获取到后进行更新,不会去b索引上获取锁,因为该SQL没有更新b索引上数据(b索引的叶子节点数据只有b和id)。此时更新聚集索引是直接更新, 但更新a索引是先删后插的形式,以此来保证索引更新前后的有序性
-
update table set id = 1 where a = 2;该SQL会先去a索引获取a=2锁,然后去聚集索引获取锁,然后去b索引上获取锁(隐式锁),全部获取到后进行更新,更新各个索引的数据页的id值,此时更新聚集索引是先删后插保证索引有序性,a和b索引都是直接更新对应记录的id即可
-
update table set c = 3 where a = 2;该SQL会先去a索引获取a=2的锁,然后去聚集索引获取锁,然后更新c
删除操作会上什么锁?
和更新操作差不多,根据where后面的条件,看先去哪个索引上删除,例如 where id = 1,就先去聚集索引获取锁,聚集索引获取到锁后再去各个辅助索引获取到对应的锁,只有获取到全部索引上的锁后才能真正的删除,否则阻塞;如果是where a = 1,那就先去a这个索引上获取锁,获取到后再去聚集索引获取锁,然后再去其它索引树上获取,同样只有获取到全部索引上的锁后才能真正的删除,否则阻塞
-
对于唯一索引:如果要删的数据不存在,则上间隙锁,锁住不存在的那块间隙;如果存在,则上记录锁
-
对于非唯一索引:如果要删的数据不存在,则上间隙锁,锁住不存在的那块间隙;如果存在,上临键锁(可重复读及以上隔离级别),开始扫描符合条件的记录,这时如果发生阻塞(记录锁之间的阻塞,间隙锁可以共存),就直接阻塞,直到对方释放锁,然后才继续扫描剩下符合条件的记录,上锁算法与上面相同,也会对扫到的数据去聚集索引上上锁
select … lock in share mode 和 select … for update 有什么区别
select … lock in share mode,上的是共享锁:
-
where后面是主键——则在主键上进行锁算法扫描
-
where后面是辅助索引——如果索引覆盖,那么就只在本索引树上加锁;如果需要回表,那就还需要去主键索引上锁(看似有缺陷,主键索引读写冲突无法阻塞)
select … for update,上的是独占锁:
-
where后面是主键——则在主键上进行锁算法扫描
-
where后面是辅助索引——不管需不需要回表,在本索引树上获取锁后,都需要去主键索引上锁
共享锁和独占锁?
-
共享锁和共享锁之间互相兼容,可以同时存在
-
独占锁和其他任何锁都不兼容,不能同时存在
-
一个资源上已经有共享锁时,不能再加独占锁
-
一个资源上已经有独占锁时,不能再加任何锁
插入操作的流程?
有锁冲突(插入意向锁与间隙锁的锁冲突)的情况:
加插入意向锁——等待其它事务的间隙锁释放——其它事务的间隙锁释放——插入一条数据
无锁冲突(插入的点所处的区间没有间隙锁)的情况:
插入一条数据
可以看到,插入意向锁只有在不能直接插入数据,需要等待其它事务释放间隙锁,也就是发生锁互斥的情况下,才会存在!
插入完成,但事务还没提交,如果这时候有其他事务来更改插入的这一行数据会怎样?
事务既然还没提交,那么就不应该让其它事务更改,但我们根据上面的插入流程发现,是没有手段防止其它事务更改的,这就用到了隐式锁
设插入的数据叫数据行a,当其他事务有操作到a时(如更改a,删除a,插入a,select…lock in share mode,select…for update)时,判断a上是否存在活跃的事务,如果存在,则其它事务为 insert 事务创建一个排他记录锁,并将自己加入到锁等待队列;
也就是说insert不会给插入的记录上锁,但是其它事务会判断insert的那个事务还有没有存活,有的话帮他上个记录锁,然后把自己阻塞住,等待insert的那个事务提交或回滚,这种记录锁就是隐式锁。不由insert的事务上锁,而是其它事务帮其上锁
如果其它事务在本事务插入后,把插入的这个间隙上了个间隙锁,此时本事务对插入的数据的记录锁也会显露出来,也是其它事务帮忙上的,一旦显露出来,就是记录锁,而不是插入意向锁了