并发控制
预期读者
1. 初学者。
2. 想复习的人。
3. 原来学习并发觉得太复杂,没搞清楚,想以更简单的方式学习的人。
4. 懒得总结的人。
下面,我就以一个比较简单的方式来总结,和你们分享。:)
目标
并发控制要做的是协调并发事务的执行,保证数据完整不受破坏,同时避免用户得到不正确的数据。主要目标有以下两个:
1. 保证事务的特性:ACID(Automicity,Consistency,Isolation,Durability)。不再赘述。
2. 尽量多的并发数。
问题
并发操作如果不加任何控制,则可能存在以下的问题:
1. 丢失更新。(“写”到“完”之间有另一人“写”)
2. 读过时的数据。(“读”到“完”之间有另一人“写”)
3. 读脏的数据。(“写”到“撤”之间有另一人“读”)
括号里面的语句,使用比较接近本质的现象,来给前面的问题进行简单的描述或定义。
锁机制
并发控制的主要方法是采用封锁技术:
要解决问题1和3,我们需要在写的时候,不让其他人有任何的权限。这就设计出了第一种锁:排他型锁(X封锁)。这种锁只允许一个事务独享数据,其实事务只有等这个事务解除封锁后,才能对数据进行任何类型的封锁。
要解决问题2,我们需要在读的时候,不让其他人有写的权限。这就有了第二种锁:共享型锁(S封锁)。我们可以理解为:排写型锁。
这样一看,其实就是我们常用的读写锁,这是Net下我比较喜欢用的读写锁类:线程同步:System.Core中新的读写锁。
封锁协议
一级封锁协议。“事务T在修改数据R之前必须先对其加X锁,走到事务结束才释放。一级封锁协议可防止丢失更新,并保证事务T是可恢复的。但不能保证可重复读和不读脏数据。”。解决了1,没解决2、3,这是因为没有对读的事务进行任务控制。
二级封锁协议。“一级封锁协议上加上事务T在读取数据R之前对其加S锁,读完后即可释放S锁。二级封锁协议可防止丢失更新,还可防止读脏数据。但不能保证可重复读。”。解决了1、3,没解决2,这是因为读后就释放,到事务完之前的这段时间有可能被写。
三级封锁协议。“一级封锁协议上加上事务T在读取数据R之前对其加S锁,走到事务结束后才释放。三级封锁协议可防止丢失更新、防止读脏数据与数据可重复读。”。解决1,2,3,这是因为它弥补了二级封锁协议的缺点。
两段封锁协议。“所有事务必须分两个阶段对数据项加锁和解锁。其中扩展阶段是在对任何数据进行读、写操作之前,首先要申请并获得对该数据的封锁;收缩阶段是在释放一个封锁之后,事务不能再申请和获得任何其他封锁。若并发执行的所有事务均遵守两段协议,则对这些事务的任何并发高度策略都是可串行化的(可以避免丢失更新、不可重复读和读脏数据等问题)。遵守两段封锁协议的事务可能会发生死锁”。解决了1,2,3,却可能会死锁。原因:“串行化”方案自然可以解决与之含义相反的“并发”所带来的问题,但是同时因为它其实已经不是并发了,所以也就不可能达到第二个目标:“高并发”。死锁发生在两个事务在扩展阶段都想要申请对方已经拥有的资源。
结束语
上面的并发方案是比较基础型的,所以并未提及应用级别的乐观锁、悲观锁等。从问题出发,理解锁的设计原理,再理解封锁协议的设计原理,可以让我们在应用的时候,根据不同的情况,选择不同的锁机制和封锁协议以满足不同的需求。这个才是最终的目的。
因为打算明年试试去考一个职称,再加上也想扎实一下一些很有用的基础知识,所以最近开始看《系统架构设计师考试全程指导》这本书。中途学到并发时,感觉很难想明白为什么会有这些锁,为什么会有这些协议,为什么每个协议就可以解决这些特定的问题。所以花了些时间研究了下,写出来和大家分享。上面许多原因是基于我的理解,如果不对的话,欢迎拍砖,并指出错误的原因。谢谢。:)