一、事务

定义:事务是访问和更新数据库的程序执行单元,事务中包含一条或者多条SQL语句,这些语句要么全部执行成功,要么都不执行。

在MySQL中,事务支持是在引擎层实现的,MySQL是一个支持多引擎的系统,但并不是所有的引擎都支持事务,比如MySQL原生的MyISAM引擎就不支持事务,而InnoDB很好的支持事务。

事务的ACID特性是原子性、一致性、隔离性、持久性。按照严格的标准,只有同时满足ACID特性的才是事务,但是在各大数据库厂商的实现中,真正满足ACID特性的事务极少。InnoDB引擎默认事务隔离级别是“可重复读”,未满足隔离性;Oracle默认的事务隔离级别是“读提交”,也不满足隔离性...

隔离性与隔离级别

隔离性是事务特性之一,当数据库上有多个事务同时执行的时候,就有可能出现“脏读”、“不可重复读”、“幻读”的问题。为了解决相应的这些问题,提出了“隔离级别”的概念。

SQL标准的隔离级别包括:读未提交,读提交,可重复读,串行化。需要明确的是,隔离的越严实,效率就会越低,因此很多时候我们需要在二者之间找到一个平衡点。

  • 读未提交:一个事务还未提交,它做的变更就能被别的事务看见。
  • 读提交:一事务在提交之后,它做的变更才能被其他事务看见。
  • 可重复读:一个事务在执行过程中看到的数据总是和这个事务在启动时看见的数据是一致的。
  • 串行化:对于同一记录,“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成才能继续执行。(类似操作系统中对资源的互斥访问)

实现上述隔离级别原理简述:

“读未提交”直接返回记录上的最新值;“读提交”和“可重复读”在不同的时候建立视图,访问的时候以视图的逻辑结果为准。“读提交”在SQL语句开始执行的时候建立视图,“可重复读”在事务启动的时候创建视图,在整个事务存在期间都使用的这个视图;“串行化”直接用“加锁”的方式避免并行访问。

二、ACID特性及实现原理

1.原子性

原子性是指一个事务是一个不可分割的工作单位,其中的操作要么都做,要么都不做。如果事务中的某一条SQL执行失败,则已经执行的语句必须回滚,数据库退回到执行事务之前的状态。

实现原理:undo log(回滚日志)

InnoDB存储引擎提供了两种事务日志:

  • redo log(重做日志)
  • undo log(回滚日志)

redo log用于保持事务持久性,undo log是事务原子性和隔离性的实现基础。

  • 当事务对数据库进行修改的时候,InnoDB会生成对应的 undo log

  • 事务执行失败或者调用了rollback,事务进行回滚,利用 undo log 中的信息将数据回滚到修改之前的样子。

undo log 属于逻辑日志,它记录的是SQL执行相关的信息。当发生回滚的时候,InnoDB会根据 undo log 的内容做与之前相反的工作:

  • 对于每个 insert ,回滚的时候执行 delete
  • 对于每个 delete ,回滚的时候执行 insert
  • 对于每个 update ,回滚的时候会执行一个相反的 update 把数据该回去

以 update 为例,当事务执行 update 的时候,生成的 undo log 中会包含被修改行的主键(便于知道修改了哪些行)、修改了哪些列、及这些列在修改前后的值等信息,回滚的时候便可以利用这些信息将数据还原到 update 之前的状态。

2.一致性

一致性是指事务执行后,数据库的完整性约束没有被破坏,事务执行前后都是合法的数据状态。

数据库的完整性约束包括但不限于:

  • 实体完整性(如行的主键存在且唯一)
  • 列完整性(如字段类型、大小、长度符合要求)
  • 外键约束
  • 用户自定义完整性(如转账前后两个账户的余额和不变)

可以说,一致性是事务追求的最终目标。原子性、持久性、隔离性都是为了保证数据库状态的一致性。此外,除了数据库层面的保障,一致性的实现也需要应用层面进行保障。

实现:

  • 保证原子性、持久性、隔离性
  • 数据库本身提供保障,如不允许向整形列插入字符串值,字符串长度不超过列限制等
  • 应用层面的保证

3.隔离性

隔离性是指事务内部的操作与其他事务都是隔离的,并发执行的各个事务之间不能相互干扰

严格的隔离性,对应了事务隔离级别中的“可串行化”,但实际应用中出于性能方面的考虑很少会使用可串行化。

考虑简单的读写操作(不考虑带锁读等特殊操作),对隔离性的讨论分以下两个方面:

  • (事务A)写操作对(事务B)写操作的影响:锁机制保证隔离性
  • (事务A)写操作对(事务B)读操作的影响:MVCC保证隔离性

①锁机制

隔离性要求同一时刻只能有一个事务对数据进行写操作,InnoDB存储引擎即是通过锁机制来保证这一点。

(1) 锁机制的基本原理概括:

a.事务在修改数据之前,获得相应的锁。

b.获得锁之后,事务方可以修改数据。

c.在该事务操作期间,这部分数据是锁定的,其他事务想要修改数据,需要等待当前事务提交或者回滚释放锁。

(2) 按照粒度,锁可以分为表锁、行锁以及在二者之间的其他锁。表锁在操作时会锁定整张表,并发性能较差;行锁只锁定待操作的数据,并发性能好。由于加锁本身会消耗资源(获取锁,检查锁,释放锁都要消耗资源),因此在锁定数据较多的情况下选择使用表锁可以节省大量资源。

MySQL中不同的存储引擎支持的锁不一样,比如MyISAM只支持表锁,InnoDB同时支持表锁和行锁,且处于性能方面的考虑,绝大多数情况下都选择使用行锁。

(3) 查看锁信息(基于InnoDB存储引擎)

select * from information_schema.innodb_locks; #查看锁的概况
show engine innodb status; #InnoDB整体状态,包括锁的情况

②MVCC(多版本并发控制协议)

(1) 在介绍MVCC之前,先看在并发情况下,读操作存在的三种问题:

  • 脏读:当前事务(A)可以读到其他事务(B)未提交的数据(这种数据称 脏数据)

  • 不可重复读:在事务A中先后两次读取同一个数据,两次读取到的数据不一致

  • 幻读:在事务A中按照某个条件先后两次查询数据库,两次查询结果的记录总条数不一致

脏读与不可重复读的区别:前者读取到的是其他事务未提交的数据,后者读到的是其他事务已经提交的数据。

不可重复读与幻读的区别:前者是数据自身变了,后者是数据的行数变了。

隔离级别与读问题的关系:

InnoDB存储引擎默认的隔离级别是可重复读(RR)。在SQL标准中,RR是无法避免幻读的,但是InnoDB中实现的RR却避免了幻读。

(2) RR解决上述三个问题,使用的是MVCC,即多版本的并发控制协议。

MVCC特点是在同一时刻,不同的事务读取到的数据可能是不同的(即多版本)。MVCC最大的优点是不加锁,因此读写不冲突,并发性能好。InnoDB实现的MVCC多个版本的数据可以共存,主要依靠的是隐藏列(也可以称之为标记位)和 undo log。其中隐藏列包括该行数据的版本号、删除时间、指向undo log 的指针等。

在 MySQL 中,实际上每条记录在更新的时候都会同时记录一条回滚操作。记录上的最新值,通过回滚操作,都可以得到前一个状态的值。

假设一个值从 1 被按顺序改成了 2、3、4,在回滚日志里面就会有类似下面的记录。

当前值是 4,但是在查询这条记录的时候,不同时刻启动的事务会有不同的 read-view。如图中看到的,在视图 A、B、C 里面,这一个记录的值分别是 1、2、4,同一条记录在系统中可以存 在多个版本,就是数据库的多版本并发控制(MVCC)。对于 read-view A,要得到 1,就必须 将当前值依次执行图中所有的回滚操作得到。同时,即使现在有另外一个事务正在将 4 改成 5,这个事务跟 read-view A、B、C 对 应的事务是不会冲突的。

所以解决脏读与不可重复读问题使用简单的MVCC即可,InnoDB 实现的 RR 还要使用 next-keylock 机制一起来避免了幻读现象。

(3) 当没有事务再需要用到这些回滚日志时,回滚日志会被删除。什么时候才不需要了呢?就是当系统里没有比这个回滚日志更早的 read-view 的时候。

由此我们得到一点注意事项:长事务意味着系统里面会存在很老的事务视图。由于这些事务随时可能访问数据库里面的任何数 据,所以这个事务提交之前,数据库里面它可能用到的回滚记录都必须保留,这就会导致大量占用存储空间。

4.持久性

持久性是指事务一旦提交,他对数据库的改变就是永久性的,接下来的其他操作或者故障不应该对其有任何影响。

实现原理:redo log

(1) redo log 和 undo log 都属于 InnoDB 的事务日志。下面介绍redo log的由来:

InnoDB 作为 MySQL 的存储引擎,数据是存放在磁盘中的,但如果每次读写数据都需要磁盘 IO,效率会很低。为此,InnoDB 提供了缓存(Buffer Pool),Buffer Pool 中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:

  • 当从数据库读取数据时,会首先从 Buffer Pool 中读取,如果 Buffer Pool 中没有,则从磁盘读取后放入 Buffer Pool。
  • 当向数据库写入数据时,会首先写入 Buffer Pool,Buffer Pool 中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)。

Buffer Pool 的使用大大提高了读写数据的效率,但是也带来了新的问题:如果 MySQL 宕机,而此时 Buffer Pool 中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。

于是,redo log 被引入来解决这个问题:当数据修改时,除了修改 Buffer Pool 中的数据,还会在 redo log 记录这次操作;当事务提交时,会调用 fsync 接口对 redo log 进行刷盘。

如果 MySQL 宕机,重启时可以读取 redo log 中的数据,对数据库进行恢复:

  • redo log 采用的是 WAL(Write-ahead logging,预写式日志),所有修改先写入日志,再更新到 Buffer Pool,保证了数据不会因 MySQL 宕机而丢失,从而满足了持久性要求。

(2) 问题:既然 redo log 也需要在事务提交时将日志写入磁盘,为什么它比直接将 Buffer Pool 中修改的数据写入磁盘(即刷脏)要快呢?

主要有以下两方面的原因:

  • 刷脏是随机 IO,因为每次修改的数据位置随机,但写 redo log 是追加操作,属于顺序 IO。

  • 刷脏是以数据页(Page)为单位的,MySQL 默认页大小是 16KB,一个 Page 上一个小修改都要整页写入;而 redo log 中只包含真正需要写入的部分,无效 IO 大大减少。

(3) redo log 与 binlog

我们知道,在 MySQL 中还存在 binlog(二进制日志)也可以记录写操作并用于数据的恢复,但二者是有着根本的不同的。

作用不同:

  • redo log 是用于 crash recovery 的,保证 MySQL 宕机也不会影响持久性;
  • binlog 是用于 point-in-time recovery 的,保证服务器可以基于时间点恢复数据,此外 binlog 还用于主从复制。

层次不同:

  • redo log 是 InnoDB 存储引擎实现的,
  • binlog 是 MySQL 的服务器层实现的,同时支持 InnoDB 和其他存储引擎。

内容不同:

  • redo log 是物理日志,内容基于磁盘的 Page。
  • binlog 是逻辑日志,内容是一条条 sql。

写入时机不同:

  • redo log 的写入时机相对多元。前面曾提到,当事务提交时会调用 fsync 对 redo log 进行刷盘;这是默认情况下的策略,修改innodb_flush_log_at_trx_commit 参数可以改变该策略,但事务的持久性将无法保证。

    除了事务提交时,还有其他刷盘时机:如 master thread 每秒刷盘一次 redo log 等,这样的好处是不一定要等到 commit 时刷盘,commit 速度大大加快。
  • binlog 在事务提交时写入。

MySQL事务及ACID特性的更多相关文章

  1. 深入学习MySQL事务:ACID特性的实现原理

    事务是MySQL等关系型数据库区别于NoSQL的重要方面,是保证数据一致性的重要手段.本文将首先介绍MySQL事务相关的基础概念,然后介绍事务的ACID特性,并分析其实现原理. MySQL博大精深,文 ...

  2. [MySQL] 事务的ACID特性

    事务的ACID特性: 原子性(atomicity):一个事务是一个不可分割的最小工作单位,事务中的所有操作要么都做,要么都不做. 一致性(consistency):事务前后数据的完整性必须保持一致.事 ...

  3. 一文说尽MySQL事务及ACID特性的实现原理

    MySQL 事务基础概念 事务(Transaction)是访问和更新数据库的程序执行单元:事务中可能包含一个或多个 sql 语句,这些语句要么都执行,要么都不执行.作为一个关系型数据库,MySQL 支 ...

  4. MySQL 学习笔记(一)MySQL 事务的ACID特性

    MySQL事务是什么,它就是一组数据库的操作,是访问数据库的程序单元,事务中可能包含一个或者多个 SQL 语句.这些SQL 语句要么都执行.要么都不执行.我们知道,在MySQL 中,有不同的存储引擎, ...

  5. 深入理解大数据之——事务及其ACID特性

    目录 事务简介 事物的定义 事务的目的 事务的状态 事务的ACID属性 ACID简介 原子性(Atomicity) 一致性(Consistency) 隔离性(Isolation) 持久性(Durabi ...

  6. 数据库中事务的ACID特性

    数据库中事务的ACID特性 前言前面我们介绍过数据库中 带你了解数据库中JOIN的用法 与 带你了解数据库中group by的用法 的相关用法.本章节主要来介绍下数据库中一个非常重要的知识点事务,也是 ...

  7. Oracle事务的ACID特性

    Oracle事务的ACID特性 1.原子性(Atomicity) 事务的原子性是指事务中包含的所有操作要么都做,要么都不做,保证数据库是一致的. 例如:A帐户向B帐户划账1000,则先将A减少1000 ...

  8. 分布式事务(ACID特性、CAP定律)

    普通事务和分布式事务的区别: 普通事务就是一般所说的数据库事务,事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成.当事务被提交给了DBMS(数据库管理系统),则DBMS(数 ...

  9. MySQL InnoDB存储引擎事务的ACID特性

    1.前言 相信工作了一段时间的同学肯定都用过事务,也都听说过事务的4大特性ACID.ACID表示原子性.一致性.隔离性和持久性.一个很好的事务处理系统,必须具备这些标准特性: 原子性(Atomicit ...

随机推荐

  1. 网页基础:网页设计(我所知道的所有的html和css代码(含H5和CSS3)),如有错误请批评指正

    最基础的网页设计,就是给你一个图片你做成一个网页,当然,我的工作是C#,个人网页的功底不是很高首先先认识一下网页的一些相关知识: 一般的,现在一个html网页一般包含html文件,css文件,js文件 ...

  2. PCA与LDA介绍

    PCA(主成分分析) PCA是一种无监督降维方式,它将数据投影到一组互相正交的loading vectors(principal axes)之上,并保证投影后的点在新的坐标轴上的方差最大 记数据集\( ...

  3. ABP学习笔记(1)-使用mysql

    前言 开始学习ABP啦 下载官方模板 ​ 下载地址: https://aspnetboilerplate.com/Templates ​ 我这边选择的是.NET Core+VUE 移除SqlServe ...

  4. 开发小白也毫无压力的hexo静态博客建站全攻略 - 躺坑后亲诉心路历程

    目录 基本原理 方法1 - 本机Windows下建站 (力荐) 下载安装node.js 用管理员权限打开命令行,安装hexo-cli和hexo 下载安装git 初始化hexo 使用hexo gener ...

  5. 《前端之路》之 前端图片 类型 & 优化 & 预加载 & 懒加载 & 骨架屏

    目录 09: 前端图片 类型 & 优化 & 预加载 & 懒加载 & 骨架屏 09: 前端图片 类型 & 优化 & 预加载 & 懒加载 & ...

  6. 论文学习-深度学习目标检测2014至201901综述-Deep Learning for Generic Object Detection A Survey

    目录 写在前面 目标检测任务与挑战 目标检测方法汇总 基础子问题 基于DCNN的特征表示 主干网络(network backbone) Methods For Improving Object Rep ...

  7. Caffe源码理解2:SyncedMemory CPU和GPU间的数据同步

    目录 写在前面 成员变量的含义及作用 构造与析构 内存同步管理 参考 博客:blog.shinelee.me | 博客园 | CSDN 写在前面 在Caffe源码理解1中介绍了Blob类,其中的数据成 ...

  8. 刨根问底:if 后怎么就可以跟对象,变量交换写法是语法糖吗?

    1.万物皆可布尔 一般语言中的 if 语句语法是这样的: if (条件表达式){    执行语句} 而在 Python 中,if 后面不仅可以是条件表达式,还可以是任意对象.例如: my_list = ...

  9. 使用LXD搭建Web网站

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由独木桥先生 发表于云+社区专栏 介绍 Linux的容器是Linux的一组进程,通过使用Linux内核功能与系统隔离.它是一个类似于虚拟 ...

  10. 玩转Spring Cloud之服务注册发现(eureka)及负载均衡消费(ribbon、feign)

    如果说用Spring Boot+Spring MVC是开发单体应用(或单体服务)的利器,那么Spring Boot+Spring MVC+Spring Cloud将是开发分布式应用(快速构建微服务)的 ...