MySQL 原理篇

MySQL 索引机制

MySQL 体系结构及存储引擎

MySQL 语句执行过程详解

MySQL 执行计划详解

MySQL InnoDB 缓冲池

MySQL InnoDB 事务

MySQL InnoDB 锁

MySQL InnoDB MVCC

MySQL InnoDB 实现高并发原理

MySQL InnoDB 快照读在RR和RC下有何差异

转载:《InnoDB并发如此高,原因竟然在这?》

并发控制

为啥要进行并发控制?

并发的任务对同一个临界资源进行操作,如果不采取措施,可能导致不一致,故必须进行并发控制(Concurrency Control)。

技术上,通常如何进行并发控制?

通过并发控制保证数据一致性的常见手段有:

  • 锁(Locking)
  • 数据多版本(Multi Versioning)

如何使用普通锁保证一致性?

  1. 操作数据前,锁住,实施互斥,不允许其他的并发任务操作;
  2. 操作完成后,释放锁,让其他任务执行;

如此这般,来保证一致性。

普通锁存在什么问题?

简单的锁住太过粗暴,连“读任务”也无法并行,任务执行过程本质上是串行的。

于是出现了共享锁与排他锁:

  • 共享锁(Share Locks,记为S锁),读取数据时加S锁
  • 排他锁(eXclusive Locks,记为X锁),修改数据时加X锁

共享锁与排他锁的玩法是:

  • 共享锁之间不互斥,简记为:读读可以并行
  • 排他锁与任何锁互斥,简记为:写读,写写不可以并行

可以看到,一旦写数据的任务没有完成,数据是不能被其他任务读取的,这对并发度有较大的影响。

有没有可能,进一步提高并发呢?

即使写任务没有完成,其他读任务也可能并发,这就引出了数据多版本。

数据多版本

数据多版本是一种能够进一步提高并发的方法,它的核心原理是:

  1. 写任务发生时,将数据克隆一份,以版本号区分;
  2. 写任务操作新克隆的数据,直至提交;
  3. 并发读任务可以继续读取旧版本的数据,不至于阻塞;

如上图:

  1. 最开始数据的版本是V0;
  2. T1时刻发起了一个写任务,这是把数据clone了一份,进行修改,版本变为V1,但任务还未完成;
  3. T2时刻并发了一个读任务,依然可以读V0版本的数据;
  4. T3时刻又并发了一个读任务,依然不会阻塞;

可以看到,数据多版本,通过“读取旧版本数据”能够极大提高任务的并发度。

提高并发的演进思路,就在如此:

  • 普通锁,本质是串行执行
  • 读写锁,可以实现读读并发
  • 数据多版本,可以实现读写并发

好,对应到InnoDB上,具体是怎么玩的呢?

redo、undo、回滚段

在进一步介绍 InnoDB 如何使用“读取旧版本数据”极大提高任务的并发度之前,有必要先介绍下 redo 日志,undo 日志,回滚段(rollback segment)。

为什么要有redo日志?

数据库事务提交后,必须将更新后的数据刷到磁盘上,以保证 ACID 特性。磁盘随机写性能较低,如果每次都刷盘,会极大影响数据库的吞吐量。

优化方式是,将修改行为先写到 redo 日志里(此时变成了顺序写),再定期将数据刷到磁盘上,这样能极大提高性能。

这里的架构设计方法是,随机写优化为顺序写,思路更重要。

假如某一时刻,数据库崩溃,还没来得及刷盘的数据,在数据库重启后,会重做 redo 日志里的内容,以保证已提交事务对数据产生的影响都刷到磁盘上。

一句话,redo 日志用于保障,已提交事务的 ACID 特性。

为什么要有undo日志?

数据库事务未提交时,会将事务修改数据的镜像(即修改前的旧版本)存放到 undo 日志里,当事务回滚时,或者数据库奔溃时,可以利用 undo 日志,即旧版本数据,撤销未提交事务对数据库产生的影响。

  • 对于 insert 操作,undo 日志记录新数据的 PK(ROW_ID),回滚时直接删除;
  • 对于 delete/update 操作,undo 日志记录旧数据 row,回滚时直接恢复;
  • 他们分别存放在不同的 buffer 里。

一句话,undo 日志用于保障,未提交事务不会对数据库的 ACID 特性产生影响。

什么是回滚段?

存储 undo 日志的地方,是回滚段。

undo 日志和回滚段和 InnoDB 的 MVCC 密切相关,这里举个例子展开说明一下。

表:t(id PK, name);

数据为:

1, shenjian

2, zhangsan

3, lisi

此时没有事务未提交,故回滚段是空的。

接着启动了一个事务:

start trx;
delete (1, shenjian);
update set(3, lisi) to (3, xxx);
insert (4, wangwu);

可以看到:

  • 被删除前的 (1, shenjian) 作为旧版本数据,进入了回滚段;
  • 被修改前的 (3, lisi) 作为旧版本数据,进入了回滚段;
  • 被插入的数据,PK(4) 进入了回滚段;

接下来,假如事务 rollback,此时可以通过回滚段里的 undo 日志回滚,假设事务提交,回滚段里的 undo 日志可以删除。

可以看到:

  • 被删除的旧数据恢复了;
  • 被修改的旧数据也恢复了;
  • 被插入的数据,删除了;

事务回滚成功,一切如故。

InnoDB是基于多版本并发控制的存储引擎

MVCC 就是通过“读取旧版本数据”来降低并发事务的锁冲突,提高任务的并发度。

核心问题:

  1. 旧版本数据存储在哪里?
  2. 存储旧版本数据,对 MySQL 和 InnoDB 原有架构是否有巨大冲击?

通过上文 undo 日志和回滚段的铺垫,这两个问题就非常好回答了:

  1. 旧版本数据存储在回滚段里;
  2. 对 MySQL 和 InnoDB 原有架构体系冲击不大;

InnoDB 的内核,会对所有 row 数据增加三个内部属性:

  1. DB_TRX_ID,6字节,记录每一行最近一次修改它的事务 ID;
  2. DB_ROLL_PTR,7字节,记录指向回滚段 undo 日志的指针;
  3. DB_ROW_ID,6字节,单调递增的行 ID;

InnoDB 为何能够做到这么高的并发?

回滚段里的数据,其实是历史数据的快照(snapshot),这些数据是不会被修改,select 可以肆无忌惮的并发读取他们。

快照读(Snapshot Read),这种一致性不加锁的读(Consistent Nonlocking Read),就是 InnoDB 并发如此之高的核心原因之一。

这里的一致性是指,事务读取到的数据,要么是事务开始前就已经存在的数据(当然,是其他已提交事务产生的),要么是事务自身插入或者修改的数据。

什么样的 select 是快照读?

除非显示加锁,普通的select语句都是快照读,例如:

select * from t where id>2;

这里的显示加锁,非快照读是指:

select * from t where id>2 lock in share mode;
select * from t where id>2 for update;

总结

  • 常见并发控制保证数据一致性的方法有锁,数据多版本;
  • 普通锁串行,读写锁读读并行,数据多版本读写并行;
  • redo 日志保证已提交事务的 ACID 特性,设计思路是,通过顺序写替代随机写,提高并发;
  • undo 日志用来回滚未提交的事务,它存储在回滚段里;
  • InnoDB 是基于 MVCC 的存储引擎,它利用了存储在回滚段里的 undo 日志,即数据的旧版本,提高并发;
  • InnoDB 之所以并发高,快照读不加锁;
  • InnoDB 所有普通 select 都是快照读;

本文的知识点均基于 MySQL5.6。

MySQL InnoDB 实现高并发原理的更多相关文章

  1. Innodb 实现高并发、redo/undo MVCC原理

    一.并发控制   因为并发情况下有可能出现不同线程对同一资源进行变动,所以必须要对并发进行控制以保证数据的同一与安全.   可以参考CPython解释器中的GIL全局解释器锁,所以说python中没有 ...

  2. InnoDB高并发原理

    一.并发控制 为啥要进行并发控制? 并发的任务对同一个临界资源进行操作,如果不采取措施,可能导致不一致,故必须进行并发控制(Concurrency Control). 技术上,通常如何进行并发控制? ...

  3. 浅析Mysql InnoDB存储引擎事务原理

    浅析Mysql InnoDB存储引擎事务原理 大神:http://blog.csdn.net/tangkund3218/article/details/47904021

  4. 搞懂MySQL InnoDB事务ACID实现原理

    前言 说到数据库事务,想到的就是要么都做修改,要么都不做.或者是ACID的概念.其实事务的本质就是锁和并发和重做日志的结合体.那么,这一篇主要讲一下InnoDB中的事务到底是如何实现ACID的. 原子 ...

  5. nginx、swoole高并发原理初探

    阅前热身 为了更加形象的说明同步异步.阻塞非阻塞,我们以小明去买奶茶为例. 同步与异步 同步与异步的重点在消息通知的方式上,也就是调用结果通知的方式. 同步:当一个同步调用发出去后,调用者要一直等待调 ...

  6. PHP利用Mysql锁解决高并发

    前面写过利用文件锁来处理高并发的问题的,现在我们说另外一个处理方式,利用Mysql的锁来解决高并发的问题 先看没有利用事务的时候并发的后果 创建库存管理表 CREATE TABLE `storage` ...

  7. 【原创】记一次MySQL大表高并发写入引发CPU飙升的排障过程

    目录 一.故障现象... 1 二.初步分析... 2 三.排障过程... 2 1.排查是否QPS或insert并发请求上升导致问题发生... 2 2.排查是否锁资源等待或block导致了insert变 ...

  8. 一文快速搞懂MySQL InnoDB事务ACID实现原理(转)

    这一篇主要讲一下 InnoDB 中的事务到底是如何实现 ACID 的: 原子性(atomicity) 一致性(consistency) 隔离性(isolation) 持久性(durability) 隔 ...

  9. 【多线程与高并发原理篇:4_深入理解synchronized】

    1. 前言 越是简单的东西,在深入了解后发现越复杂.想起了曾在初中阶段,语文老师给我们解说<论语>的道理,顺便给我们提了一句,说老子的无为思想比较消极,学生时代不要太关注.现在有了一定的生 ...

随机推荐

  1. 常用windows命令

    目录 本教程概述 用到的工具 标签 简介 1.cmd的一些规则 2.cd切换目录命令 3.dir显示目录命令 4.type显示文本内容 5.del 删除文件 6.查看IP地址 7.net 命令 8.n ...

  2. [Abp vNext 源码分析] - 11. 用户的自定义参数与配置

    一.简要说明 文章信息: 基于的 ABP vNext 版本:1.0.0 创作日期:2019 年 10 月 23 日晚 更新日期:暂无 ABP vNext 针对用户可编辑的配置,提供了单独的 Volo. ...

  3. 生产环境中利用软链接避免"rm -rf /"的方法

      1.将系统中的rm二进制文件重命名为rm_real:2.编写脚本rm_shell,rm_shell中主要包含以下内容:    2.1)路径转换模块,用于将rm_shell参数中的路径转换为绝对路径 ...

  4. python中生成器与迭代器

    可迭代对象:一个实现了iter方法的对象是可迭代的 迭代器:一个实现了iter方法和next方法的对象就是迭代器 生成器都是Iterator对象,但list.dict.str虽然是Iterable(可 ...

  5. 利用Arthas定位线上问题实例

    前言 Arthas是一个类似于Btrace的JVM在线调试分析工具,具体可参考我之前写的一篇博客:利用JVM在线调试工具排查线上问题.本文分享笔者刚遇到的一个问题,虽然不复杂,但是很典型. 问题与分析 ...

  6. 02--Java Jshell的使用 最适合入门的Java教程

    JShell JShell目标 Java Shell 工具(简称:JShell)是一个用于学习Java编程语言和构建Java代码原型的交互式工具.JShell是一个Read-Evaluate-Prin ...

  7. mp-vue拖拽组件的实现

    作为一个效率还不错的小前端,自己的任务做完之后真的好闲啊,千盼万盼终于盼来了业务的新需求,他要我多加一个排序题,然后用户通过拖拽来排序,项目经理看我是个实习生,说有点复杂做不出来就算了,我这么闲的一个 ...

  8. Beetlex服务框架之Webapi版本访问控制

    在应用服务中API更新是很普遍的事情,为了服务良好地运作很多时候需要新旧版本同时兼容:为了应对这一系列的需求FastHttpApi在新版中强化了Url重写机制来支持API访问版本控制,由原来固定的重写 ...

  9. C/C++语言误区void main( )

    很多人甚至市面上的一些书籍,都使用了void main( ) ,其实这是错误的.C/C++ 中从来没有定义过void main( ) .C++ 之父 Bjarne Stroustrup 在他的主页上的 ...

  10. unity 截屏总结

    转载与https://www.cnblogs.com/MissLi/p/8005342.html 1.针对指定的相机进行截屏 此中方式要添加yield return waitfortheEndofFr ...