大家好,我是热心的大肚皮,皮哥。
锁的前世
上一篇我们聊了事务在并发执行时可能引发一致性的各种现象,大概分为3种。
- 读-读情况:并发事务相继读取相同的记录,读取不会修改信息,也不会引起什么问题,所以允许这种情况的发生。
- 写-写情况:并发事务相继对相同的记录进行改动。在这种情况下会发生脏写的现象,任何隔离级别都不允许发生,所以在多个未提交事务相继对一条记录修改时,需要让他们排队,那怎么记录呢,会采用如下的数据结构。这只是把重要的属性拿了出来,trx信息代表这个数据结构与哪个事务有关,is_waiting代表当前事务是否在等待,true代表等待中。
- 读-写或写-读情况:一个事务读取数据,另一个事务进行改动。之前说过,这种情况下会出现脏读、不可重复读、幻读的现象。
那怎么避免呢?有两种可选的解决方案。
- 读操作使用多版本并发控制(MVCC),写操作进行加锁。其中事务利用MVCC进行的读取操作也叫一致性读。
- 读、写操作都采用上面的数据结构来控制并发的情况。
其实,上面的数据结构也就是锁。
锁的分类
行锁
针对记录的锁分为两类,如下。
- 共享锁(Shared Lock):简称S锁,事务要读取一条记录时,需要先获取该记录的S锁。
- 独占锁(Exclusive Lock):常称为排他锁,简称X锁。在事物要改动一条记录时,需要先获取该记录的X锁。
其中,S锁与S锁是兼容的,S锁与X锁是不兼容的,X锁与X锁也是不兼容的。
表锁
对于表来说,除了有上面两种行锁之外,额外多了一种叫意向锁的东西。如下。
- 意向共享锁(Intention Shared Lock):简称IS锁,当事务准备在某条记录上加S锁时,需要先加个表级别的IS锁。
- 意向独占锁(Intention Exclusive Lock):简称IX锁,当事务准备在某条记录上加X锁时,需要先加个表级别的IX锁。
为什么要这么操作呢?其实不难想象,我们操作记录的时候会打上行锁,那么当我们进行表的操作时,根据意向锁可以使效率更高效,不然只能遍历。
总结一下兼容性。如下。
兼容性 | X | IX | S | IS |
---|---|---|---|---|
X | 不兼容 | 不兼容 | 不兼容 | 不兼容 |
IX | 不兼容 | 兼容 | 不兼容 | 不兼容 |
S | 不兼容 | 不兼容 | 兼容 | 兼容 |
IS | 不兼容 | 兼容 | 兼容 | 兼容 |
看到这个大家是不是有点晕,以大学教室为例。
- 教室一般都是公用的,我们可以随便选一间教室进去上自习。每当一个同学进去上自习,就相当于在教室门口上挂了一把S锁,如果很多同学都去上自习,就相当于教室门口挂了很多把S锁(类似行级别的S锁)。
- 有时教室会进行检修,例如换灯管、换地板啥的,这些项目不能同时开展。如果针对某个项目进行维修,就不允许来上自习,也不允许其他项目维修,相当于教室门口挂了把X锁(类似行级别的X锁)。
除了针对教室而言,也还会有其他的需求。
- 有上级领导要来参观教学楼,校领导不想影响同学们上自习,但是此时不能有教室在维修,于是在教学楼门口放置了一把S锁(类型表级别的S锁)。此时:
- 来上自习的学生看到门口有S锁,可以继续进入教学楼自习。
- 修理工看到有S锁,则在门口等着,啥时候上级领导走了,把教学楼门口的S锁撤掉后,才能进入维修。
- 学校想占用教学楼进行考试,此时不允许教学楼中有正在上自习的教室,也不允许对教室维修,于是可以在教学楼门口放置一把X锁(类型表的X锁)。此时:
- 来上自习的学生看到教学楼门口有X锁,则需要在教学楼门口等着,啥时候考试结束,把教学楼的X锁撤掉,在进入教学楼上自习。
- 修理工看到教学楼门口有X锁,则需要在教学楼门口等着,啥时候考试结束,把教学楼的X锁撤掉,在进入教学楼维修。
但是这样有一个问题,如果撤掉X锁,我们需要把所有的教室的情况都要摸清楚,这样性能太差了,所以引入了意向锁的概念。
- 如果学生上自习,那么在教学楼上加一把IS锁,然后在教室上加S锁。
- 如果维修工维修教室,则先在教学楼加上一把IX锁,然后在教室上加X锁。
之后:
- 上级领导要参观教学楼,需要先看看教学楼有没有IX锁,如果有则代表有教室在维修,需要维修结束后把IX锁撤掉才可以在教学楼上加S锁。
- 如果有考试要占用教学楼,则需要看看有没有IS锁与IX锁,如果有则代表有学生上自习或者有教室在维修,需要结束后才可以在整栋楼上加X锁。