MySQL事务及ACID特性
一、事务
定义:事务是访问和更新数据库的程序执行单元,事务中包含一条或者多条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特性的更多相关文章
- 深入学习MySQL事务:ACID特性的实现原理
事务是MySQL等关系型数据库区别于NoSQL的重要方面,是保证数据一致性的重要手段.本文将首先介绍MySQL事务相关的基础概念,然后介绍事务的ACID特性,并分析其实现原理. MySQL博大精深,文 ...
- [MySQL] 事务的ACID特性
事务的ACID特性: 原子性(atomicity):一个事务是一个不可分割的最小工作单位,事务中的所有操作要么都做,要么都不做. 一致性(consistency):事务前后数据的完整性必须保持一致.事 ...
- 一文说尽MySQL事务及ACID特性的实现原理
MySQL 事务基础概念 事务(Transaction)是访问和更新数据库的程序执行单元:事务中可能包含一个或多个 sql 语句,这些语句要么都执行,要么都不执行.作为一个关系型数据库,MySQL 支 ...
- MySQL 学习笔记(一)MySQL 事务的ACID特性
MySQL事务是什么,它就是一组数据库的操作,是访问数据库的程序单元,事务中可能包含一个或者多个 SQL 语句.这些SQL 语句要么都执行.要么都不执行.我们知道,在MySQL 中,有不同的存储引擎, ...
- 深入理解大数据之——事务及其ACID特性
目录 事务简介 事物的定义 事务的目的 事务的状态 事务的ACID属性 ACID简介 原子性(Atomicity) 一致性(Consistency) 隔离性(Isolation) 持久性(Durabi ...
- 数据库中事务的ACID特性
数据库中事务的ACID特性 前言前面我们介绍过数据库中 带你了解数据库中JOIN的用法 与 带你了解数据库中group by的用法 的相关用法.本章节主要来介绍下数据库中一个非常重要的知识点事务,也是 ...
- Oracle事务的ACID特性
Oracle事务的ACID特性 1.原子性(Atomicity) 事务的原子性是指事务中包含的所有操作要么都做,要么都不做,保证数据库是一致的. 例如:A帐户向B帐户划账1000,则先将A减少1000 ...
- 分布式事务(ACID特性、CAP定律)
普通事务和分布式事务的区别: 普通事务就是一般所说的数据库事务,事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成.当事务被提交给了DBMS(数据库管理系统),则DBMS(数 ...
- MySQL InnoDB存储引擎事务的ACID特性
1.前言 相信工作了一段时间的同学肯定都用过事务,也都听说过事务的4大特性ACID.ACID表示原子性.一致性.隔离性和持久性.一个很好的事务处理系统,必须具备这些标准特性: 原子性(Atomicit ...
随机推荐
- MySQL 复制 - 性能与扩展性的基石 2:部署及其配置
正所谓理论造航母,现实小帆船.单有理论,不动手实践,学到的知识犹如空中楼阁.接下来,我们一起来看下如何一步步进行 MySQL Replication 的配置. 为 MySQL 服务器配置复制非常简单. ...
- PdfReader按页将PDF切割成多个PDF
private MemoryStream GetNewPdfByPageNum(PdfReader pdfReader, int pageNum) { MemoryStream memoryStrea ...
- 补习系列(16)-springboot mongodb 数据库应用技巧
目录 一.关于 MongoDB 二.Spring-Data-Mongo 三.整合 MongoDB CRUD A. 引入框架 B. 数据库配置 C. 数据模型 D. 数据操作 E. 自定义操作 四.高级 ...
- 解决 Vue 动态生成 el-checkbox 点击无法赋值问题
博客地址:https://ainyi.com/68 最近遇到一个问题,在一个页面需要动态渲染页面内的表单,其中包括 checkbox 表单类型,并且使用 Element 组件 UI 时,此时 v-mo ...
- Python中最好用的命令行解析工具:argparse
Python 做为一个脚本语言,可以很方便地写各种工具.当你在服务端要运行一个工具或服务时,输入参数似乎是一种硬需(当然你也可以通过配置文件来实现). 如果要以命令行执行,那你需要解析一个命令行参数解 ...
- 设计模式 | 模板方法模式(template method)
定义: 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中.模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 结构:(书中图,侵删) 一个定义整体框架的父类 若干不同具体实现 ...
- Android底部导航栏(可滑动)----TabLayout+viewPager
[TabLayout] ①TabLayout是选项卡,在屏幕空间有限的情况下,对不同的空间进行分组.属于android support design,更多的用于新闻上,如果放在底部也可做底部导航栏 ② ...
- ubuntu安装mysql没有让我设置密码
终端输入: sudo cat /etc/mysql/debian.cnf显示内容:# Automatically generated for Debian scripts. DO NOT TOUCH! ...
- Exp5 Msf基础应用 20164312 马孝涛
1.本实践目标是掌握metasploit的基本应用方式,重点常用的三种攻击方式的思路.具体需要完成: 1.1一个主动攻击实践,如ms08_067; (1分) 1.2 一个针对浏览器的攻击,如ms11_ ...
- Fiddler-弱网测试设置
第一步:打开模拟弱网环境 第二步:打开配置文件 第三步:修改配置参数 m_SimulateModem,修改后最好 Ctrl+S 保存一下 第四步:修改好参数返回后需要再次打开弱网环境 以上弱网设置就 ...