分布式数据库学习--分布式并发控制
前言
并发控制理论在分布式计算领域是一块较为重要的内容。了解和掌握好这块理论知识,将有助于我们在工作实践中解决一些复杂的难题。最近笔者学习了分布式数据库系统原理书中的分布式并发控制的相关内容,本文借此机会总结总结。
并发控制的目标
在往常,我们一提到并发这个词,就会马上联想到多线程这个词。这是因为我们在平时的开发工作中经常有遇到多线程的场景。但是回过头来看这个词,其实并发的场景绝不仅限于多线程操作,另外一个典型的应用场景就是数据库里的事务更新。现在一个核心的问题来了,并发控制的目的是为了什么?它有什么作用?我们为什么要引入这样的控制机制呢?这些问题归纳起来就是一个答案:在大规模并行操作下,保证数据的正确更新。也就是说如果我们不加以进行控制,是可能发生数据更新异常的情况,比如说操作乱序、操作丢失等等的情况。
并发控制机制的分类
OK,那么并发控制机制中到底有哪些机制呢?在我们平常熟悉的多线程编程模型里,我们提到最多的控制技巧就是两种:1.悲观方式,即锁控制机制,从比较粗粒度的锁(synchronized)到细粒度的锁(ReadLock、WriteLock)。2.乐观方式,即加时间戳方式。加时间戳的方式是为了保证操作的有序性。
但是在并发控制理论体系内,对于并发控制机制进行了更为细致的分类,主要为以下的一些分类:
首先它同样有分为悲观算法和乐观算法。
悲观算法:悲观算法包括基于加锁,基于排序以及混合前2者的方式。在这里笔者要说的是,这里的基于排序是一个比较宽泛的词,比如说基于时间戳的排序也可以算是一种基于排序的方式。
乐观算法:乐观算法同样可以分为基于加锁和基于时间戳的方式。
其实从上面的分类结果可以看出,在并发控制理论体系内,并不是仅仅凭借是否加锁/时间戳来判断并发控制方式是否为悲观/乐观的算法。真实的情况是2种方式都可以交叉使用,目标是为了增强在当前算法下的并发控制。比如说我们可以在基于加锁的算法内也使用时间戳,这样可以提高效率和并发的级别。
下面,我们来学习并发理论体系内对于加锁,加时间戳实现方式的一些细节内容。
基于加锁的并发控制算法
基于加锁并发控制算法的一个核心关键词是锁。在这里,任何涉及到数据更新的操作都必须申请到锁单元。持有了这个锁之后,表明你是当前数据的更改权,其它操作必须等待当前操作结束,释放掉锁之后,才能进行。所以在这里,我们可以看出,这的确是一种偏向悲观的方式,因为它假设程序运行时总会有很多冲突的操作。
加锁方式有一个比较大的缺点是它的效率问题,因为加锁意味着当前只会允许一个操作对数据进行有效的更新。但并不是所有的操作并行执行时都会有冲突,比如说2个读操作。因此我们有了读写锁的概念,读写锁可以说是一种细粒度的锁了,至少在效率上要比之前所提到的单一的锁要高。
基于时间戳的并发控制算法
基于时间戳的并发控制算法是另外一种比较常用的并发控制方式。从某种层面上来说,基于加锁的方式是一种看起来是通过互斥的方式进行来维护数据的串行化。而基于时间戳的方式则是利用了时间戳本身的特点来达到数据操作的串行化。因为我们知道时间戳是具有单调性和唯一性的,所以它能够很好的对每个新创建的事务分配标识。后面我们可以通过时间戳标识进行排序,来达到正确更新的效果。在基于时间戳的并发控制算法内,有下面的形式化规则定义:
TO(timestamp ordering)规则:给定事务Ti和Tk中的两个冲突操作Oij和Ojk,当且仅当ts(Ti)< ts(Tk)时,Oij会在Ojk之前执行。在这种情况下,Ti为较旧的事务,而Tk为较新的事务。
使用TO规则的调度程序会将每一个操作与调度过的冲突操作进行比较,如果新的操作属于较新的事务,则接受它,否则就将它拒绝,然后将相应的事务赋予新的时间戳并进行重启。这句话大家细细品味。、
悲/乐观并发控制算法的执行步骤
最后我们来看看悲/乐观并发控制算法的执行步骤,来看看二者的一个区别。
首先是悲观算法下的执行过程:
|有效性验证|-->|读|-->|计算|-->|写|
而乐观算法是将有效性验证的操作移到写操作之前,如下所示:
|读|-->|计算|-->|有效性验证|-->|写|
我们可以看到,乐观算法的执行步骤可以让前面的读、计算操作可以自由的执行,只要在最后提交更新的时候进行一步检查即可。
参考资料
[1].分布式数据库系统原理,第11章,分布式并发控制