多版本号并发控制(MVCC)在实际项目中的应用
近期项目中遇到了一个分布式系统的并发控制问题。该问题能够抽象为:某分布式系统由一个数据中心D和若干业务处理中心L1,L2 … Ln组成;D本质上是一个key-value存储,它对外提供基于HTTP协议的CRUD操作接口。L的业务逻辑能够抽象为以下3个步骤:
- read: 依据keySet {k1, … kn}从D获取keyValueSet {k1:v1, … kn:vn}
- do: 依据keyValueSet进行业务处理,得到须要更新的数据集keyValueSet’ {k1′:v1′, … km’:vm’} (注:读取的keySet和更新的keySet’可能不同)
- update: 把keyValueSet’更新到D (注:D保证在一次调用更新多个key的原子性)
在没有事务支持的情况下。多个L进行并发处理可能会导致数据一致性问题。比方。考虑L1和L2的例如以下运行顺序:
- L1从D读取key:123相应的值100
- L2从D读取key:123相应的100
- L1将key:123更新为100 + 1
- L2将key:123更新为100 + 2
假设L1和L2串行运行,key:123相应的值将为103,但上面并发运行中L1的运行效果全然被L2所覆盖,实际key:123所相应的值变成了102。
解决方式1:基于锁的事务
为了让L的处理具有可串行化特性(Serializability),一种最直接的解决方式就是考虑为D加上基于锁的简单事务。让L在进行业务处理前先锁定D,完毕以后释放锁。另外,为了防止持有锁的L因为某种原因长时间未提交事务。D还须要具有超时机制,当L尝试提交一个已超时的事务时会得到一个错误响应。
本方案的长处是实现简单,缺点是锁定了整个数据集。粒度太大;时间上包括了L的整个处理时间,跨度太长。尽管我们能够考虑把锁定粒度减少到数据项级别,按key进行锁定,但这又会带来其它的问题。因为更新的keySet’可能是事先不确定的,所以可能无法在開始事务时锁定全部的key。假设分阶段来锁定须要的key。又可能出现死锁(Deadlock)问题。另外,按key锁定在有锁争用的情况下并不能解决锁定时间太长的问题。
所以,按key锁定仍然存在重要的不足之处。
解决方式2:多版本号并发控制
为了实现可串行化。同一时候避免锁机制存在的各种问题。我们能够採用基于多版本号并发控制(Multiversion concurrency control。MVCC)思想的无锁事务机制。人们一般把基于锁的并发控制机制称成为悲观机制,而把MVCC机制称为乐观机制。这是因为锁机制是一种预防性的,读会堵塞写。写也会堵塞读,当锁定粒度较大。时间较长时并发性能就不会太好;而MVCC是一种后验性的,读不堵塞写。写也不堵塞读。等到提交的时候才检验是否有冲突。因为没有锁,所以读写不会相互堵塞,从而大大提升了并发性能。我们能够借用源码版本号控制来理解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的唯一client;所以,P的cache管理是非常easy的,不必像多client情形操心缓存的失效。只是,实际上,据我所知redis和Amazon SimpleDB都已经有了Conditional Update的支持。
悲观锁和MVCC对照
上面介绍了悲观锁和MVCC的基本原理,可是对于它们分别适用于什么场合。不同的场合下两种机制优劣详细表如今什么地方还不是非常清楚。
这里我就对一些典型的应用场景进行简单的分析。须要注意的是以下的分析不针对分布式。悲观锁和MVCC两种机制在分布式系统、单数据库系统、甚至到内存变量各个层次都存在。
### 场景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解决分布式系统并发控制问题的方法。和基于悲观锁的方法相比,该方法避免了大粒度和长时间的锁定,能更好地适应对读的响应速度和并发性要求高的场景。
參考
- Wikipedia – Serializability
- Wikipedia – Compare-and-swap
- Wikipedia – Multiversion concurrency control
- Lock-free algorithms: The try/commit/(try again) pattern
- Amazon SimpleDB FAQs – Does Amazon SimpleDB support transactions?
- redis – Transactions
- A Quick Survey of MultiVersion Concurrency Algorithms
- 非堵塞算法思想在关系数据库应用程序开发中的使用
多版本号并发控制(MVCC)在实际项目中的应用的更多相关文章
- 多版本号并发控制(MVCC)在分布式系统中的应用
QQ群:289150599 问题 近期项目中遇到了一个分布式系统的并发控制问题.该问题能够抽象为:某分布式系统由一个数据中心D和若干业务处理中心L1,L2 ... Ln组成:D本质上是一个key-va ...
- 转: 多版本并发控制(MVCC)在分布式系统中的应用 (from coolshell)
from: http://coolshell.cn/articles/6790.html 问题 最近项目中遇到了一个分布式系统的并发控制问题.该问题可以抽象为:某分布式系统由一个数据中心D和若干业务 ...
- 在VS项目中通过GIT生成版本号作为编译版本号
上一篇博客写了如何在 .Net 项目使用 SVN 作为版本控制工具时生成与代码对应的组件版本号.虽然在公司一直使用 SVN ,但我却对 GIT 情有独钟(可能要归功于那段捣鼓 ROM 的时光),但少有 ...
- 在VS项目中使用SVN版本号作为编译版本号
在实际项目中(特别是作为产品的项目),版本号是必不可少的一部分.版本号的规则也有许多种,在此不讨论具体的编码规范.对于迭代的产品,版本繁多,特别是有多个实施项目所使用产品的版本不同(基于定制需求)时, ...
- 如何查看.Net源代码vs版本号以及C#项目中各文件的含义
查看.Net源代码vs版本号以及C#项目中各文件的含义 用记事本打开vs项目的.sln文件. 第2行就是这个源代码包的开发软件vs版本号了 注意了,如果是vs2003的sln文件通常没有这行,可以判断 ...
- 开发日记:在VS项目中使用SVN版本号作为编译版本号
在实际项目中(特别是作为产品的项目),版本号是必不可少的一部分.版本号的规则也有许多种,在此不讨论具体的编码规范.对于迭代的产品,版本繁多,特别是有多个实施项目所使用产品的版本不同(基于定制需求)时, ...
- 为什么在SpringBoot+maven的项目中,所引入的依赖包可以不指定依赖的版本号?
当在Springboot项目中引入了spring-boot-starter-parent,则可以不用引入依赖包版本号,比如: <parent> <groupId>org.spr ...
- 多War项目中静态文件的共享方案
[原创申明:文章为原创,欢迎非盈利性转载,但转载必须注明来源] 在互联网产品中,一般会有多个项目(Jar.WAR)组成一个产品线.这些WAR项目,因为使用相同的前端架构(jQuery.easyui等) ...
- Maven项目中的pom.xml详解【转】
什么是pom? pom作为项目对象模型.通过xml表示maven项目,使用pom.xml来实现.主要描述了项目:包括配置文件:开发者需要遵循的规则,缺陷管理系统,组织和licenses,项目的url, ...
随机推荐
- [Apple开发者帐户帮助]一、开始(2)登录您的开发者帐户
使用Apple ID登录您的开发者帐户.如果您注册了付费程序(Apple Developer Program或Apple Developer Enterprise Program),请使用您用于注册的 ...
- 浅谈自学Python之路(购物车程序练习)
购物车程序练习 今天我们来做一个购物车的程序联系,首先要理清思路 购物车程序需要用到什么知识点 需要用到哪些循环 程序编写过程中考虑值的类型,是int型还是字符串 如果值为字符串该怎么转成int型 用 ...
- Elasticsearch之批量操作bulk
1.bulk相当于数据库里的bash操作. 2.引入批量操作bulk,提高工作效率,你想啊,一批一批添加与一条一条添加,谁快? 3.bulk API可以帮助我们同时执行多个请求 4.bulk的格式: ...
- 【java并发】(1)深入理解volatile关键字
volatile这个关键字可能很多朋友都听说过,或许也都用过.在Java 5之前,它是一个备受争议的关键字,因为在程序中使用它往往会导致出人意料的结果.在Java 5之后,volatile关键字才得以 ...
- 努比亚(nubia) M2青春版 NX573J 解锁BootLoader 并进入临时recovery ROOT
努比亚(nubia) M2青春版 NX573J 解锁BootLoader 并进入临时recovery ROOT 工具下载链接:https://pan.baidu.com/s/1NfRTdXtdAZRi ...
- hibernate_02_session介绍
什么是session? hibernate的session对象就相当于jdbc的connection.我们对数据库的操作(增删改等)都是使用的session方法. 写一个java类 package c ...
- windows常用的cmd命令和常用操作。
这几日部署Jenkins,牵扯到很多东西,比如用到许多cmd命令和Linux命令.查找比较花时间,因此将查看的文档留下,以避免下次重新查找浪费时间. Windows cmd命令: http://blo ...
- 如何修改yii2.0用户登录使用的user表为其它的表
这只是自己练习的一个记录而已. 因为某种原因,不想用yii自带的user表,想用自己建的admin数据库表,修改如下: 1. 参考高级模板里里的common\models\User 修改 Admi ...
- GrepWin:Win7下的文本替换工具
工作环境退回到Win7之后,内容查找功能非常不给力,推荐一个文本内容查找工具grepWin. Win7下的文本查找/替换工具: grepWin
- 三维重建:SLAM的尺度和方法论问题
百度百科的定义.此文引用了其他博客的一些图像,如有侵权,邮件联系删除. 作为算法的SLAM,被称为同步相机位姿确定和地图构建.作为一个工程的SLAM,有众多的算法. 在计算机视觉中, 三维重建是指根据 ...