并发控制技术手段之多版本(三)


<并发控制之多版本>
html

为了实现可串行化,同时避免锁机制存在的各类问题,咱们能够采用基于多版本并发控制(Multiversion concurrency control,MVCC)思想的无锁并发机制。人们通常把基于锁的并发控制机称成为悲观机制,而把MVCC等机制称为乐观机制。这是由于锁机制是一种预防性的,读会阻塞写,写也会阻塞读,当锁定粒度较大,时间较长是并发性能就不会太好;而MVCC是一种后验性的,读不阻塞写,写也不阻塞读,等到提交的时候才检验是否有冲突,因为没有锁,因此读写不会相互阻塞,从而大大提高了并发性能。redis

咱们能够借用源代码版本控制来理解MVCC,每一个人均可以自由地阅读和修改本地的代码,相互之间不会阻塞,只在提交的时候版本控制器会检查冲突,并提示merge。目前,Oracle、PostgreSQL和MySQL都已支持基于MVCC的并发机制,但具体实现各有不一样。数据库

MVCC的一种简单实现是基于CAS(Compare-and-swap)思想的有条件更新(Conditional Update)。普通的update参数只包含了一个keyValueSet',Conditional Update在此基础上加上了一组更新条件conditionSet { ... data[keyx]=valuex, ... },即只有在D知足更新条件的状况下才将数据更新为keyValueSet';不然,返回错误信息。这样,L就造成了以下图所示的Try/Conditional Update/(Try again)的处理模式:缓存

虽然对单个L来说不能保证每次都成功更新,但从整个系统来看,老是有任务可以顺利进行。这种方案利用Conditional Update避免了大粒度和长时间的锁定,当各个业务之间资源争用不大的状况下,并发性能很好。不过,因为Conditional Update须要更多的参数,若是condition中value的长度很长,那么每次网络传送的数据量就会比较大,从而致使性能降低。特别是当须要更新的keyValueSet'很小,而condition很大时,就显得很是不经济。网络

为了不condition太大所带来的性能问题,能够为每条数据项增长一个int型的版本号字段,由D维护该版本号,每次数据有更新就增长版本号;L在进行Conditional Update时,经过版本号取代具体的值。并发

另外一个问题是上面的解决方案假设了D是能够支持Conditional Update的;那么,若是D是一个不支持Conditional Update的第三方的key-value存储怎么办呢?这时,咱们能够在L和D之间增长一个P做为代理,全部的CRUD操做都必须通过P,让P来进行条件检查,而实际的数据操做放在D。这种方式实现了条件检查和数据操做的分离,但同时下降了性能,须要在P中增长cache,提高性能。因为P是D的惟一客户端;因此,P的cache管理是很是简单的,没必要像多客户端情形担忧缓存的失效。不过,实际上,据我所知redis和Amazon SimpleDB都已经有了Conditional Update的支持。分布式

锁机制和MVCC对比

上面介绍了锁机制和MVCC的基本原理,可是对于它们分别适用于什么场合,不一样的场合下两种机制优劣具体表如今什么地方还不是很清楚。这里我就对一些典型的应用场景进行简单的分析。须要注意的是下面的分析不针对分布式,锁机制和MVCC两种机制在分布式系统、单数据库系统、甚至到内存变量各个层次都存在。ide

场景1:对读的响应速度要求高

有一类系统更新特别频繁,而且对读的响应速度要求很高,如股票交易系统。在锁机制下,写会阻塞读,那么当有写操做时,读操做的响应速度就会受到影响;而MVCC不存在读写锁,读操做是不受任何阻塞的,因此读的响应速度会更快更稳定。性能

场景2:读远多于写

对于许多系统来说,读操做的比例每每远大于写操做,特别是某些海量并发读的系统。在锁机制下,当有写操做占用锁,就会有大量的读操做被阻塞,影响并发性能;而MVCC能够保持比较高且稳定的读并发能力。代理

场景3:写操做冲突频繁

若是系统中写操做的比例很高,且冲突频繁,这时就须要仔细评估。假设两个有冲突的业务L1和L2,它们在单独执行是分别耗时t1,t2。在锁机制下,它们的总时间大约等于串行执行的时间:

T = t1 + t2

而在MVCC下,假设L1在L2以前更新,L2须要retry一次,它们的总时间大约等于L2执行两次的时间(这里假设L2的两次执行耗时相等,更好的状况是,若是第1次能缓存下部分有效结果,第二次执行L2耗时是可能减少的):

T’ = 2 * t2

这时关键是要评估retry的代价,若是retry的代价很低,好比,对某个计数器递增,又或者第二次执行能够比第一次快不少,这时采用MVCC机制就比较适合。反之,若是retry的代价很大,好比,报表统计运算须要算几小时甚至一天那就应该采用锁机制避免retry。

从上面的分析,咱们能够简单的得出这样的结论:对读的响应速度和并发性要求比较高的场景适合MVCC;而retry代价越大的场景越适合锁机制。

总结

本文介绍了一种基于多版本并发控制(MVCC)思想的Conditional Update解决分布式系统并发控制问题的方法。和锁机制相比,该方法避免了大粒度和长时间的锁定,能更好地适应对读的响应速度和并发性要求高的场景。


备注:转载自http://www.kuqin.com/system-analysis/20120319/319108.html