最近通过《高性能MySQL》一书学习MySQL方面的知识,在看到书中所讲InnoDB-MVCC部分的时候,有一种强烈的感觉,这不就是乐观锁吗(入门级小学徒的疑惑脸)?当下便去网上以各种方式查找阅读MVCC和乐观锁相关的博客,发现大部分的博客对于这两者之间的关系都只字不提,提了的也是众说纷纭,关于两者关系的细节方面也十分暧昧没有定论。在暂时无法得出最终结论的情况下,我先谈谈在学习这方面知识后我自己对两者的理解,然后试着得出自己的结论,正确与否大家一起思考。

在解释MVCC之前,我首先引用《高性能MySQL》书中原文来解释一下隔离级别:

READ UNCOMMITTED(未提交读):在READ UNCOMMITTED级别,事务中的修改,即使没有提交,对其他事务也都是可见的。事务可以读取未提交的数据,这也被称为脏读。

READ COMMITTED(提交读):大多数数据库默认的隔离级别(MySQL除外)。在READ COMMITTED级别,一个事务开始时,只能“看见”已经提交的事务所做的修改。换句话说,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的,这个级别有时候也叫做不可重复读,因为同一事务中两次执行同样的查询,可能会得到不一样的结果。

REPEATABLE READ(可重复读):这是MySQL默认的隔离级别,解决了脏读的问题。该级别保证了在同一事务中多次读取同样记录的结果是一致的。但是理论上,该隔离级别还是无法解决另外一个幻读的问题。所谓幻读,指的是当某个事务在读取某个范围内的记录时,另外一个事务又在该范围内插入了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。

SERIALIZABLE(可串行化):该隔离级别下通过强制事务串行执行,避免了幻读的问题。简单来说,SERIALIZABLE会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。

上面解释隔离级别时提到了,在REPEATABLE READ隔离级别下,尽管解决了不可重复读,但还是存在幻读的问题。如果要避免幻读,就得在事务执行的时候加锁,但是大量的锁会严重影响性能。怎样才能不通过加锁还能解决幻读呢?这就是MVCC要做的事情。

MVCC是Multi-Version Concurrency Control(多版本并发控制)的缩写,很多数据库都实现了MVCC,但是在不同的存储引擎中MVCC的实现是不同的,今天所说的是InnoDB中的MVCC实现。InnoDB的MVCC,是通过在每行记录后保存两个隐藏的列来实现的(用户不可见)。一个列保存行创建的时间,一个列保存行过期(删除)的时间,这里所说的时间并不是传统意义上的时间,而是系统版本号,下面是REPEATABLE READ隔离级别下MVCC的具体操作:
-SELECT
InnoDB会根据以下两个条件检查每行记录:
(1)InnoDB只查找版本早于当前事务版本的数据行(行的系统版本号小于或者等于事务的系统版本号),这样可以确保事务读取到的行,要么是在事务开始之前已经存在的,要么是事务自身插入或者修改过的(结合以下INSERT、UPDATE操作理解)。
(2)行的删除版本要么未定义,要么大于当前事务版本号。可以确保事务读取到的行,在事务开启之前未被删除(结合以下DELETE操作理解)。
-INSERT
InnoDB为新插入的每一行保存当前系统版本号作为行版本号。
-DELETE
InnoDB为删除的每一行保存当前系统版本号作为行删除标识(第二个隐藏列的作用来了)。
-UPDATE
InnoDB将更新后的列作为新的行插入数据库(并不是覆盖),并保存当前系统版本号作为该行的行版本号,同时保存当前系统版本号到原来的行作为行删除标识。

到这里,MVCC是什么以及它做了什么事基本上已经说清楚了,为什么在学习了MVCC后我会产生“这就是乐观锁”的想法呢(实际上很多人都有这种想法,在一些博客里也有人说MVCC就是乐观锁)?有这几个原因。首先,InnoDB中MVCC和乐观锁(其实这么说是不严谨的,后面会解释为什么)都是通过“不加锁”的手段来实现加锁的效果。其次它们的不加锁手段都是通过版本号去控制的。通过这两点也不难看出为什么会有很多人在MVCC和乐观锁之间产生疑问。

那么乐观锁是怎么实现的呢?最常见的就是通过数据版本(Version)记录机制实现。数据版本和InnoDB-MVCC中的系统版本作用相似不做过多解释。通过为数据库表增加一个数字类型的字段作为版本标识Version(用户可见,字段名自定),当读取数据时,将其Version的值一同读出,数据每更新一次,Version都增加1,当提交更新的时候,判断数据库表对应行的当前版本信息与第一次读取出来的Version值进行对比,如果一致,则给与更新,否则不予更新(可以不涉及事务,但是MVCC机制必须依托于事务,事实上隔离级别本就是事务的隔离级别)。具体操作如下:

SELECT id, name, Version FROM testable;(例如id=1,Version=1024)
UPDATE testable SET name=’张三’,Version=Version+1 WHERE id=1 AND Version=1024;

从上面的所有文字中,我们还是无法得出一个有效的结论,只看得出InnoDB-MVCC和文中所提到的乐观锁确实很像,它们到底是何关系我们还是无从所知。那我们再来看看《高性能MySQL》中所提到的一句话:不同存储引擎的MVCC实现是不同的,典型的有乐观并发控制和悲观并发控制。看完这句话我们再结合上文,可以得出这样一个结论:MVCC并不是乐观锁,InnoDB所实现的MVCC才是乐观锁(当然也有其他存储引擎利用乐观并发控制的思想实现MVCC),更严谨一点来说,乐观锁并不是一种具体的技术,乐观锁只是一种并发控制的思想,所有认为“并发事务不算大”而采用非加锁的形式来实现“加锁”效果的控制机制我们都认为它是乐观锁。既然如此,那我们之前提到的乐观锁就不能叫乐观锁了,它只是乐观锁的一种表达方式,是一种在用户行为上通过非加锁的方式来实现并发控制的手段。同样的MVCC更不能称之为乐观锁,只能说InnoDB实现的MVCC是一种在系统行为上通过非加锁的方式来实现并发控制的手段。

总结来说,InnoDB-MVCC是一种系统行为,在REPEATABLE READ隔离级别下,它通过乐观并发控制解决了该隔离级别所不能解决的幻读,但是前提是这些都得依托于事务的封装。尽管如此,它还是无法完全解决一些并发业务场景下的问题,并且过多的事务使用会严重影响系统的性能,这就需要通过用户行为去约束(最开始所提到的乐观锁)。

所以,最后的结论就是,MVCC并非乐观锁,但是InnoDB存储引擎所实现的MVCC是乐观的,它和之前所提到的用户行为的“乐观锁”都采用的是乐观机制,属于不同的“乐观锁”手段,它们都是“乐观家族”的成员。

原文:https://blog.csdn.net/Jeaforea/article/details/82181449

知乎:

在数据库中,并发控制是指在多个用户/进程/线程同时对数据库进行操作时,如何保证事务的一致性和隔离性的,同时最大程度地并发。

当多个用户/进程/线程同时对数据库进行操作时,会出现3种冲突情形:

  1. 读-读,不存在任何问题
  2. 读-写,有隔离性问题,可能遇到脏读(会读到未提交的数据) ,幻影读等。
  3. 写-写,可能丢失更新

要解决冲突,一种办法是是锁,即基于锁的并发控制,比如2PL,这种方式开销比较高,而且无法避免死锁。

多版本并发控制(MVCC)是一种用来解决读-写冲突的无锁并发控制,也就是为事务分配单向增长的时间戳,为每个修改保存一个版本,版本与事务时间戳关联,读操作只读该事务开始前的数据库的快照。 这样在读操作不用阻塞写操作,写操作不用阻塞读操作的同时,避免了脏读和不可重复读

乐观并发控制(OCC)是一种用来解决写-写冲突的无锁并发控制,认为事务间争用没有那么多,所以先进行修改,在提交事务前,检查一下事务开始后,有没有新提交改变,如果没有就提交,如果有就放弃并重试。乐观并发控制类似自选锁。乐观并发控制适用于低数据争用,写冲突比较少的环境。

多版本并发控制可以结合基于锁的并发控制来解决写-写冲突,即MVCC+2PL,也可以结合乐观并发控制来解决写-写冲突。

 
链接:https://www.zhihu.com/question/27876575/answer/71836010

InnoDB-MVCC与乐观锁的更多相关文章

  1. MySQL/InnoDB中,乐观锁、悲观锁、共享锁、排它锁、行锁、表锁、死锁概念的理解

    文章出处:https://www.souyunku.com/2018/07/30/mysql/?utm_source=tuicool&utm_medium=referral MySQL/Inn ...

  2. 【数据库】悲观锁与乐观锁与MySQL的MVCC实现简述

    悲观锁 悲观锁,就是一种悲观心态的锁,每次访问数据时都会锁定数据: 乐观锁 乐观锁,就是一种乐观心态的锁,每次访问数据时并不锁定数据,期待数据并没作修改,如果数据没被修改则作具体的业务 应用程序上使用 ...

  3. MYSQL中的乐观锁实现(MVCC)简析

    https://segmentfault.com/a/1190000009374567#articleHeader2 什么是MVCC MVCC即Multi-Version Concurrency Co ...

  4. 悲观锁,乐观锁以及MVCC

    在上文中,我们探讨了MySQL不同存储引擎中的各类锁,在这篇文章中我们将要讨论的是MySQL是如何实现并发控制的.并发问题有三种,分别为: 读-读,不存在任何问题 读-写,有隔离性问题,可能遇到脏读( ...

  5. innodb 悲观锁,乐观锁

    转 http://www.cnblogs.com/chenwenbiao/archive/2012/06/06/2537508.html CREATE TABLE `products` ( `id` ...

  6. 乐观锁和 MVCC 的区别?

    二者不是一个层面的东西. MVCC(Multi-Version Concurrent Control),基于快照隔离机制(Snapshot Isolations)进行多版本并发控制,是一种以乐观锁为理 ...

  7. InnoDB关于事务、锁、MVCC专题

    目录 并发所带来的的问题 脏写 脏读 不可重复读 幻读 事务 事务的特性 事务的四种隔离级别 锁 为什么要加锁 InnoDB的七种锁 不同事务RR和RC下加锁的规则 MVCC mvcc进一步提高并发 ...

  8. 通过版本号实现乐观锁(MVCC)

    乐观锁大多是基于数据版本记录的机制实现 , 如 , 为每一行数据增加一个整型版本标识(version) , 每次数据更新都把版本号+1 工作原理:读取出数据时,将此版本号一同读出,之后更新时,对此版本 ...

  9. 【mysql】关于乐观锁

    一.乐观锁介绍 乐观锁( Optimistic Locking ) 相对悲观锁而言,乐观锁假设认为数据一般情况下不会造成冲突,所以在数据进行提交更新的时候,才会正式对数据的冲突与否进行检,乐观锁适用于 ...

随机推荐

  1. git执行cherry-pick时修改提交信息

    git执行cherry-pick时修改提交信息 在本地分支执行cherry-pick命令时有时需要修改commit message信息,可以加参数-e实现: git cherry-pick -e co ...

  2. Thinkphp volist 多重循环原样输出数组key值的使用总结

    最近因为项目的缘故,要使用到volist.在这个过程中,遇到了一些小问题,主要就是volist在循环输出多重数据的时候,如何输出key.网上查阅了不少资料,很失望的是,大多资料就是粘贴复制Thinkp ...

  3. logging_modules

    老师的博客:http://www.cnblogs.com/Eva-J/articles/7228075.html#_label13 basicconfig import logging logging ...

  4. Spring的单例模式底层实现

    http://blog.csdn.net/cs408/article/details/48982085

  5. 排序学习实践---ranknet方法

    要: 1 背景      随着移动互联网的崛起,越来越多的用户开始习惯于从手机完成吃.喝.玩.乐.衣.食.住.行等各个方面的需求.打开手机,点开手淘.美团等APP,商品玲玲满目,而让用户将所有商品一页 ...

  6. svn 钩子应用 - svn 提交字符限制, 不能为空

    一.版本库钩子 1.1 start-commit  开始提交的通知 输入参数:传递给你钩子程序的命令行参数,顺序如下: 1.  版本库路径 2.  认证过的尝试提交的用户名 3.  Depth,mer ...

  7. vue router mode模式在webpack 打包上线问题

    vue-router mode模式有两种 hash和history. 1.hash —— 即地址栏 URL 中的 # 符号.比如这个 URL:http://www.abc.com/#/hello,ha ...

  8. heapsort(Java)(最小堆)

    public static void main(String[] args) { Scanner input = new Scanner(System.in); int n = input.nextI ...

  9. ExcelDna项目完整工程演示及讲解

    原始链接:http://www.cnblogs.com/Charltsing/p/ExcelDnaDemo.html ExcelDna工程演示讲课内容 1.ExcelDna是啥? 2.ExcelDna ...

  10. css3 animation(左右摆动) (放大缩小)

    左右摆动: @-webkit-keyframes roundRule{ 0%, 100%{ -webkit-transform: rotate(-15deg); } 50%{ -webkit-tran ...