数据库事务3(mvcc与innodb锁机制)
带着问题思考。
Q:
读写,写写冲突,或者说基本的dml操作 insert,update,delete,select,innodb存储引擎是如何处理的(在默认隔离级别repeatable-read)?
为什么insert ignore 和 update on duplicate key 会造成死锁? 在什么情况下会造成死锁,如何解决?
不同隔离级别的dml的加锁策略有什么区别?
S和IX锁是互斥的吗?
意向锁和插入意向锁的联系与不同。
什么时候innodb会加表锁?(默认rr隔离级别)
两阶段加锁?
1 背景知识
在阐述crud操作会涉及到那些操作时,首先需要了解一些锁,同时没有特别强调的话,默认讨论的是rr级别下的情况。
1.1 无锁(mvcc 快照读)
虽然本章讨论的是innodb加锁的情况,但还是要联系mvcc的内容。常见的 “select * from xx” 会加锁吗?select * 是全表扫描,但是此select还是会基于生成的readview,读取快照内容,所以不会阻塞 读&写,更别提加锁了。没有特殊需求直接select的话,我们用户用的查询是会走快照读的,不会锁行也不会锁表,这也是为什么innodb值得学习。
1.2 锁级别
表锁,行锁(间隙锁) 多的不说了,锁级别就是加在不同对象上的锁,表锁就是锁一张表,行锁就是锁一行,值得说的是间隙锁,会锁住多行连续(按索引算连续)数据。
1.3 锁的分类
1.3.1 独占锁X,共享锁S
独占锁和共享锁好理解,可以基本认为是常见的读写锁,类似于java中的ReentrantReadWriteLock,作用也是类似。 但实际上来说有一点特别的是并非读(select)只能加共享锁,select也可以加X锁。 手动加锁的查询有一下两种方式: select * from xx for share select * from xx for update
1.4 表锁和行锁的bridge————意向锁
意向锁需要好好唠叨一下。
按是否独占可分为:独占意向锁IX,共享意向锁IS
按名字可以简单猜测,意向锁就是表明意向,所以意向锁之间不是互斥的,对同一资源的加锁不会阻塞(事实也确实如此)。
在innodb中意向锁是表级锁(这个很重要,区分插入意向锁),是行级锁和表级锁的桥梁。为什么这么说?没有意向锁的情况下,如果要加表X锁,是不是需要查看有没有在行(或间隙)上加的锁(S或X),如果要对表加S锁,是不是需要查看有没有在行(或间隙)上加的X锁。这个判断的过程需要是原子的,且遍历的过程是需要耗时的,所以可以具象化冲突(详见ddia事务章节),在数据库表上构建出IX,IS这两种意向锁。每次对行(间隙)加锁前都先对表加上意向锁,这样如果需要加表锁,就可以仅查看此表上有没有表锁冲突,而不需要查看子对象(行)上是否有冲突。
总结来说X,S,IX,IS之间的互斥关系为
- | X | S | IX | IS |
---|---|---|---|---|
X | × | × | × | × |
S | × | √ | × | √ |
IX | × | × | √ | √ |
IS | × | √ | √ | √ |
特别的 1、IX和S锁是互斥的,简单理解下,在已经有共享表锁的情况下,如果需要对某行进行独占锁定,需要等待共享表锁释放。 举个栗子,我现在(select * from xx for share)查询整张表或者where没有命中索引,于此同时修改/删除(todo插入不清楚)某条数据,是不是我的写操作要等全表扫描结束?是的。 2、意向锁是表级锁 3、意向锁之间不会阻塞
1.5 gap锁
当事务中出现update等写操作需要读取当前的最新的已提交数据,且为了保证不发生写写的冲突(与其他事务的更新冲突),需要锁定一系列的
1.6 插入意向锁
当一个事务想要插入数据时,他需要检测当前行是否被