上文中,我们探讨了MySQL不同存储引擎中的各类锁,在这篇文章中我们将要讨论的是MySQL是如何实现并发控制的。并发问题有三种,分别为:

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

首先我们先来看一下悲观锁和乐观锁:

  • 悲观锁:假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。

悲观锁(Pessimistic Lock)实际上是悲观并发控制(Pessimistic Concurrency Control,“PCC”),顾名思义,就是对并发问题持有悲观的态度,认为每次对数据的操作都会引发并发冲突,因此悲观锁每次操作数据的时候都会上锁,以屏蔽一切可能违反数据完整性的操作。

悲观锁实际上就是运用了上一篇中提到的各类锁来实现并发控制,较为简单,但是开销比较大,而且只支持读-读并发,即对于同一个数据来说,如果采用悲观锁,那么读-写和写-写并发是不被允许的。

  • 乐观锁:假设不会发生并发冲突,只在提交操作时检查是否违反数据完整性。

乐观锁(Optimistic Lock)实际上是乐观并发控制(Optimistic Concurrency Control,“OCC”),对并发问题持有乐观的态度,认为不会发生并发冲突,因此不会上锁(所以乐观锁并不是一种锁)。但是,在提交更新的时候会判断一下在事务期间是否有其他进程更新了同一块数据。乐观锁解决了写-写冲突的无锁并发控制(注意,这边的无锁并不是真正的无锁,而是在执行过程中不加锁,在检测是否冲突的时候还是需要对数据进行加锁,但是这边加锁的时间明显少了很多)。乐观锁一般来说有以下两种实现方式:

1.使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加一。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。用下面的一张图来说明:

如上图所示,如果更新操作顺序执行,则数据的版本(version)依次递增,不会产生冲突。但是如果发生有不同的业务操作对同一版本的数据进行修改,那么,先提交的操作(图中B)会把数据version更新为2,当A在B之后提交更新时发现数据的version已经被修改了,那么A的更新操作会失败。

2.使用时间戳(timestamp)。乐观锁定的第二种实现方式和第一种差不多,同样是在需要乐观锁控制的table中增加一个字段,名称无所谓,字段类型使用时间戳(timestamp), 和上面的version类似,也是在更新提交的时候检查当前数据库中数据的时间戳和自己更新前取到的时间戳进行对比,如果一致则OK,否则就是版本冲突。

  • 悲观锁和乐观锁的应用场景

从上文可以看到:

1. 当发生并发冲突的概率比较大时,悲观锁更合适,以提高事务的成功率。

2. 当发生并发冲突的概率小时(如读多写少),乐观锁更合适,可以提高系统的吞吐量。


接下来我们来看看MVCC是什么。

MVCC的意思为多版本并发控制(Multiversion concurrency control),它解决的是读-写并发的问题。MVCC一般来说也可以看成是一种乐观机制,和间隙锁一样,它可以用来解决幻读的问题,只是间隙锁解决幻读是用使写进程阻塞的方式来进行的,而MVCC是以快照的方式来处理这一问题。不同数据库版本对MVCC的实现机制不同,在这边我们讨论InnoDB是如何进行MVCC的。

InnoDb 会为每一行记录增加两个字段,当前行创建时的版本号删除时的版本号(可以为空),事务在写一条记录时会将其拷贝一份生成这条记录的一个原始拷贝,写操作同样还是会对原记录加锁,但是读操作会读取未加锁的新记录,这就保证了读写并行。MVCC具体操作如下:

SELECT:InnoDB会根据以下两个条件检查每行记录:

1)InnoDB只查找版本早于当前事务版本的数据行(也就是,行的系统版本号小于或等于事务的系统版本号),这样可以确保事务读取的行,要么是在事务开始前已经存在的,要么是事务自身插入或者修改过的。

2)行的删除版本要么未定义,要么大于当前事务版本号。这可以确保事务读取到的行,在事务开始之前未被删除。

INSERT:InnoDB为新插入的每一行保存当前系统版本号作为行版本号。

DELETE:InnoDB为删除的每一行保存当前系统版本号作为行删除标识。

UPDATE:InnoDB为插入一行新记录,保存当前系统版本号作为行版本号,同时保存当系统的版本号为原来的行作为删除标识。

InnoDb 通过 MVCC 实现了读写并行,但是在不同的隔离级别下,读的方式也是有所区别的。首先要特别指出的是,在 read uncommit 隔离级别下,每次都是读取最新版本的数据行,所以不能用 MVCC 的多版本,而 serializable 隔离级别每次读取操作都会为记录加上读锁,也和 MVCC 不兼容,所以只有 RC 和 RR 这两个隔离级别才有 MVCC。

尽管 RR 和 RC 隔离级别都实现了 MVCC 来满足读写并行,但是读的实现方式是不一样的:RC 总是读取记录的最新版本,如果该记录被锁住,则读取该记录最新的一次快照,而 RR 是读取该记录事务开始时的那个版本。虽然这两种读取方式不一样,但是它们读取的都是快照数据,并不会被写操作阻塞,所以这种读操作称为 快照读(Snapshot Read)。快照读在InnoBD的实现中就是普通不加锁的select语句。与快照读相对应的是当前读,即处理的都是当前的数据,需要加锁,如select  ... lock in share mode,for update以及select,update和delete,在解决当前读的幻读问题时,MySQL使用了间隙锁的机制。

参考文档:

https://liuzhengyang.github.io/2017/04/18/innodb-mvcc/

http://www.cnblogs.com/chenpingzhao/p/5065316.html

https://riverdba.github.io/2017/04/01/MVCC-theory-study/

悲观锁,乐观锁以及MVCC的更多相关文章

  1. Java并发 行级锁/字段锁/表级锁 乐观锁/悲观锁 共享锁/排他锁 死锁

    原文地址:https://my.oschina.net/oosc/blog/1620279 前言 锁是防止在两个事务操作同一个数据源(表或行)时交互破坏数据的一种机制. 数据库采用封锁技术保证并发操作 ...

  2. Hibernate悲观锁/乐观锁

    如果需要保证数据访问的排它性,则需对目标数据加"锁",使其无法被其它程序修改 一,悲观锁 对数据被外界(包括本系统当前的其它事务和来自外部系统的事务处理)修改持保守态度,通过数据库 ...

  3. SQL Server 锁机制 悲观锁 乐观锁 实测解析

    先引入一些概念,直接Copy其他Blogs中的,我就不单独写了. 一.为什么会有锁 多个用户同时对数据库的并发操作时会带来以下数据不一致的问题: 1.丢失更新 A,B两个用户读同一数据并进行修改,其中 ...

  4. 最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁

    在Java并发场景中,会涉及到各种各样的锁如公平锁,乐观锁,悲观锁等等,这篇文章介绍各种锁的分类: 公平锁/非公平锁 可重入锁 独享锁/共享锁 乐观锁/悲观锁 分段锁 自旋锁 01.乐观锁 vs 悲观 ...

  5. Java最全锁剖析:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁

    乐观锁 VS 悲观锁 乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度,在Java和数据库中都有此概念对应的实际应用. 1.乐观锁 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会 ...

  6. Optimistic concurrency control 死锁 悲观锁 乐观锁 自旋锁

    Optimistic concurrency control https://en.wikipedia.org/wiki/Optimistic_concurrency_control Optimist ...

  7. 【MySQL】悲观锁&乐观锁

    悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念.本文将对这两种常见的锁机制在数据库数据上的实现进行比较系统的介绍. 悲观锁(Pessimistic Lock) 悲观锁的 ...

  8. innodb 悲观锁,乐观锁

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

  9. 谈谈mysql的悲观和乐观锁

    悲观锁与乐观锁是两种常见的资源并发锁设计思路,也是并发编程中一个非常基础的概念.之前有写过一篇文章关于并发的处理思路和解决方案,这里我单独将对这两种常见的锁机制在数据库数据上的实现进行比较系统的介绍一 ...

  10. MySQl中隔离级别和悲观锁乐观锁

    1.MySql的事物支持 MySQL的事务支持不是绑定在MySQL服务器本身,而是与存储引擎相关: MyISAM:不支持事务,用于只读程序提高性能 InnoDB:支持ACID事务.行级锁.并发 Ber ...

随机推荐

  1. Java基础(八)

    IO流 概述与分类 Java中的IO流技术主要用于传输数据.典型的应用场景有:读写本地文件,上传下载文件等.按照数据传输的方向可以分为两种: l  输入流(Input):既让数据进入应用程序中. l  ...

  2. 小记----------lombok插件idea的安装&常见注解解释及小案例

    Lombok安装插件 软件:idea 2018.3.6版本 1.打开settings

  3. 使用Python基于VGG/CTPN/CRNN的自然场景文字方向检测/区域检测/不定长OCR识别

    GitHub:https://github.com/pengcao/chinese_ocr https://github.com/xiaofengShi/CHINESE-OCR |-angle 基于V ...

  4. Codeforces 1194E. Count The Rectangles

    传送门 看到 $n<=5000$,直接暴力枚举左右两条竖线 然后考虑怎么计算高度在某个范围内,左端点小于等于某个值,右端点大于等于某个值的横线数量 直接用权值树状数组维护当前高度在某个区间内的横 ...

  5. Wizard's Tour CodeForces - 860D (图,构造)

    大意: 给定$n$节点$m$条边无向图, 不保证连通, 求选出最多邻接边, 每条边最多选一次. 上界为$\lfloor\frac{m}{2}\rfloor$, $dfs$贪心划分显然可以达到上界. # ...

  6. LintCode 6---合并排序数组 II

    import java.util.Arrays; public class Lint6 { /* * 合并两个排序的整数数组A和B变成一个新的数组.新数组也要有序. */ public static ...

  7. 禁止ios10双指缩放

    document.addEventListener('gesturestart', function(event) { event.preventDefault(); });

  8. 彻底了解websocket原理

    一.websocket与http WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算) 首先HTTP有 1 ...

  9. 从FBV到CBV一(开始)

    span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0; }.CodeMirror ...

  10. service与pod关联

    当我们创建pod时,仅仅是创建了pod,要为其创建rc(ReplicationController),他才会有固定的副本,然后为其创建service,集群内部才能访问该pod,使用 NodePort ...