多版本并发控制(Multiversion concurrency control,MVCC)是一种思想,有很多种实现方法。乐观并发控制(乐观锁)和悲观并发控制(悲观锁)是并发控制主要采用的技术手段。 在关系数据库管理系统里,悲观并发控制(又名“悲观锁”,Pessimistic Concurrency Control,缩写“PCC”)是一种并发控制的方法。 在关系数据库管理系统里,乐观并发控制(又名“乐观锁”,Optimistic Concurrency Control,缩写“OCC”)是一种并发控制的方法。
悲观锁和乐观锁对比 上面介绍了悲观锁和乐观锁的基本原理,但是对于它们分别适用于什么场合,不同的场合下两种机制优劣具体表现在什么地方还不是很清楚。这里我就对一些典型的应用场景进行简单的分析。需要注意的是下面的分析不针对分布式,悲观锁和MVCC两种机制在分布式系统、单数据库系统、甚至到内存变量各个层次都存在。
场景1:对读的响应速度要求高
有一类系统更新特别频繁,并且对读的响应速度要求很高,如股票交易系统。在悲观锁机制下,写会阻塞读,那么当有写操作时,读操作的响应速度就会受到影响;而乐观锁不存在读写锁,读操作是不受任何阻塞的,所以读的响应速度会更快更稳定。
场景2:读远多于写
对于许多系统来讲,读操作的比例往往远大于写操作,特别是某些海量并发读的系统。在悲观锁机制下,当有写操作占用锁,就会有大量的读操作被阻塞,影响并发性能;而乐观锁可以保持比较高且稳定的读并发能力。
场景3:写操作冲突频繁
如果系统中写操作的比例很高,且冲突频繁,这时就需要仔细评估。假设两个有冲突的业务L1和L2,它们在单独执行是分别耗时t1,t2。在悲观锁机制下,它们的总时间大约等于串行执行的时间: T = t1 + t2 而在乐观锁下,假设L1在L2之前更新,L2需要retry一次,它们的总时间大约等于L2执行两次的时间(这里假设L2的两次执行耗时相等,更好的情况是,如果第1次能缓存下部分有效结果,第二次执行L2耗时是可能减小的): T’ = 2 * t2 这时关键是要评估retry的代价,如果retry的代价很低,比如,对某个计数器递增,又或者第二次执行可以比第一次快很多,这时采用乐观锁机制就比较适合。反之,如果retry的代价很大,比如,报表统计运算需要算几小时甚至一天那就应该采用锁机制避免retry。 从上面的分析,我们可以简单的得出这样的结论:对读的响应速度和并发性要求比较高的场景适合乐观锁;而retry代价越大的场景越适合悲观锁机制。
要实现无锁(lock-free)的非阻塞算法有多种实现方法,其中CAS(比较与交换,Compare and swap)是一种有名的无锁算法(乐观锁算法)。 关系数据库数据库提供了"update T set FState = xx where FState = xx",执行这样的SQL,会返回一个更新行数,在jdbc或者odbc或者ADO .NET中都可以获得更新行数。上面的SQL,如果更新行数>0,则是更新成功,否则是没有进行任何更新,这是很典型的CAS。可以说,关系数据库原生支持CAS。
事务(Transaction)是并发控制的基本单位。 所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。例如,银行转帐工作:从一个帐号扣款并使另一个帐号增款,这两个操作要么都执行,要么都不执行。数据库事务必须具备ACID特性: Atomic原子性:指整个数据库事务是不可分割的工作单位。只有使事务中所有的操作执行成功,才算整个事务成功;事务中任何一个SQL语句执行失败,那么已经执行成功的SQL语句也必须回滚(Rollback),数据库状态应该退回到执行事务前的状态。 Consistency一致性:指数据库事务不能破坏关系数据的完整性以及业务逻辑上的一致性。例如对银行转帐事务,不管事务成功还是失败,应该保证事务结束后ACCOUNTS表中Tom和Jack的存款总额为2000元。 Isolation隔离性:数据库允许多个并发事务同时对其数据进行读写和修改的能力,每个事务都有各自的完整相互隔离的数据空间,即事务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。隔离性可以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括读未提交(Read uncommitted)、读提交(read committed)、可重复读(repeatable read)和串行化(Serializable)。 Durability持久性:指的是只要事务成功结束,它对数据库所做的更新就必须永久保存下来。即使发生系统崩溃,重新启动数据库系统后,数据库还能恢复到事务成功结束时的状态。 数据库管理系统(RDBMS)采用日志来保证事务的原子性、一致性和持久性。日志记录了事务对数据库所做的更新,如果某个事务在执行过程中发生错误,就可以根据日志,撤销事务对数据库已做的更新,使数据库退回到执行事务前的初始状态。 数据库管理系统采用锁机制来实现事务的隔离性。不同的数据库引擎的MMVC是不同的,不同事务隔离级别所采用的并发控制策略也是不同的,如InnoDB的:http://www.cnblogs.com/naci/p/3753644.html。当多个用户/进程/线程同时对数据库进行操作时,会出现3种冲突情形: 读-读,不存在任何问题 读-写,有隔离性问题,可能遇到脏读(会读到未提交的数据) ,幻影读等。 写-写,可能丢失更新 深入阅读推荐: http://www.hollischuang.com/archives/900 http://www.hollischuang.com/archives/943