乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据通常状况下不会形成冲突,因此在数据进行提交更新的时候,才会正式对数据的冲突与否进行检,乐观锁适用于多读的应用类型,这样能够提升吞吐量,像数据库若是提供相似于write_condition机制的其实都是提供的乐观锁。相似SVNmysql
悲观锁假定其余用户企图访问或者改变你正在访问、更改的对象的几率是很高的,所以在悲观锁的环境中,在你开始改变此对象以前就将该对象锁住,而且直到你提交了所做的更改以后才释放锁。sql
1.使用数据版本(Version)记录机制实现,这是乐观锁最经常使用的一种实现方式。何谓数据版本?即为数据增长一个版本标识,通常是经过为数据库表增长一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当咱们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,若是数据库表当前版本号与第一次取出来的version值相等,则予以更新,不然认为是过时数据数据库
2.乐观锁定的第二种实现方式和第一种差很少,一样是在须要乐观锁控制的table中增长一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version相似,也是在更新提交的时候检查当前数据库中数据的时间戳和本身更新前取到的时间戳进行对比,若是一致则OK,不然就是版本冲突编程
悲观的缺陷是不管是页锁仍是行锁,加锁的时间可能会很长,这样可能会长时间的限制其余用户的访问,也就是说悲观锁的并发访问性很差。服务器
乐观锁则认为其余用户企图改变你正在更改的对象的几率是很小的,所以乐观锁直到你准备提交所做的更改时才将对象锁住,当你读取以及改变该对象时并不加锁。可见乐观锁加锁的时间要比悲观锁短,乐观锁能够用较大的锁粒度得到较好的并发访问性能。并发
好比:若是第二个用户刚好在第一个用户提交更改以前读取了该对象,那么当他完成了本身的更改进行提交时,数据库就会发现该对象已经变化了,这样,第二个用户不得不从新读取该对象并做出更改。在乐观锁环境中,会增长并发用户读取对象的次数。mvc
版本控制系统高并发
若是数据是可变的,而且没法隔离呢?这种状况下最经常使用的两种控制就是乐观并发控制和悲观并发控制。性能
假设小张和小李想要同时修改同一个文件。若是使用乐观锁,俩人都能打开文件进行修改,若是小张先提交了内容,没有问题,他所作的改变会保存到服务器上。但小李提交时就会遇到麻烦,版本控制服务器会检测出两种修改的冲突,小李的提交会被具体,并由小李决定该如何处理这种状况(对于绝大部分版本控制软件来讲,会读取并标识出小张作的改变,而后由小李决定是否合并)。 版本控制
若是使用的是悲观锁,小张先检出(check out)文件,那么小李就没法再次检出同一文件,直到小张提交了他的改变
将乐观锁想成一种检测冲突的手段,而悲观锁是一种避免冲突的手段(严格来讲,乐观锁其实不能称之为“锁”,可是这个名字已经流传开了,那就继续使用吧) ,乐观锁能够提升并发访问的效率,可是若是出现了冲突只能向上抛出,而后重来一遍
悲观锁能够避免冲突的发生,可是会下降效率,选择使用那一种锁取决于访问频率和一旦产生冲突的严重性
若是系统被并发访问的几率很低,或者冲突发生后的后果不太严重(所谓后果应该指被检测到冲突的提交会失败,必须重来一次),可使用乐观锁,不然使用悲观锁
乐观锁的局限是只能在提交数据时才发现业务事务将要失败,并且在某些状况下,发现失败太迟的代价会很大。一个方法是使用悲观锁,它能够尽早地发现错误,但理难以编程实现,并且会下降系统的灵活性
MySQL InnoDB存储引擎,实现的是基于多版本的并发控制协议——MVCC (Multi-Version Concurrency Control) (注:与MVCC相对的,是基于锁的并发控制,Lock-Based Concurrency Control)。MVCC最大的好处,相信也是耳熟能详:读不加锁,读写不冲突。在读多写少的OLTP应用中,读写不冲突是很是重要的,极大的增长了系统的并发性能,这也是为何现阶段,几乎全部的RDBMS,都支持了MVCC
在InnoDB中,会在每行数据后添加两个额外的隐藏的值来实现MVCC,这两个值一个记录这行数据什么时候被建立,另一个记录这行数据什么时候过时(或者被删除)。 在实际操做中,存储的并非时间,而是事务的版本号,每开启一个新事务,事务的版本号就会递增。 在可重读Repeatable reads事务隔离级别下:
经过MVCC,虽然每行记录都须要额外的存储空间,更多的行检查工做以及一些额外的维护工做,但能够减小锁的使用,大多数读操做都不用加锁,读数据操做很简单,性能很好,而且也能保证只会读取到符合标准的行,也只锁住必要行
在MVCC并发控制中,读操做能够分红两类:快照读 (snapshot read)与当前读 (current read)。快照读,读取的是记录的可见版本 (有多是历史版本),不用加锁。当前读,读取的是记录的最新版本,而且,当前读返回的记录,都会加上锁,保证其余事务不会再并发修改这条记录。
在一个支持MVCC并发控制的系统中,哪些读操做是快照读?哪些操做又是当前读呢?以MySQL InnoDB为例:
快照读:简单的select操做,属于快照读,不加锁。(固然,也有例外,下面会分析)
当前读:特殊的读操做,插入/更新/删除操做,属于当前读,须要加锁。
全部以上的语句,都属于当前读,读取记录的最新版本。而且,读取以后,还须要保证其余并发事务不能修改当前记录,对读取记录加锁。其中,除了第一条语句,对读取记录加S锁 (共享锁)外,其余的操做,都加的是X锁 (排它锁)。