数据库事务的特性及其实现原理
目录
- 1. 认识事务
- 1.1. 什么是数据库事务
- 1.2. 为什么需要数据库事务
- 1.3. 数据库事务的特性
- 2. 数据库事务特性的实现原理
- 2.1. 原子性
- 2.2. 持久性
- `redo log` 与 `bin log`
- 2.3. 隔离性
- 2.3.1. 锁机制
- 2.3.2. 脏读、不可重复读和幻读
- 2.3.3. 数据库事务隔离级别
- 2.3.4. `MVCC`
- 2.3.5. 小结
- 2.4. 一致性
- 3. 总结
1. 认识事务
1.1. 什么是数据库事务
事务是访问数据库的程序执行单元;事务中可能包含一个或多个 SQL
语句,这些语句要么都执行,要么都不执行。事务的定义有几点需要解释下
- 数据库事务可以包含一个或多个
SQL
语句,这些操作构成一个逻辑上的整体 - 构成逻辑整体的这些数据库操作,要么全部执行成功,要么全部不执行
- 构成事务的所有操作,要么全都对数据库产生影响,要么全都不产生影响,即不管事务是否执行成功,数据库总能保持一致性状态
- 以上即使在数据库出现故障以及并发事务存在的情况下依然成立
1.2. 为什么需要数据库事务
转账是生活中常见的操作,比如从 A
账户转账 100
元到 B
账号。站在用户角度而言,这是一个逻辑上的单一操作,然而在数据库系统中,至少会分成两个步骤来完成
- 将
A
账户的金额减少100
元 - 将
B
账户的金额增加100
元
在这个过程中可能会出现以下问题
- 转账操作的第一步执行成功,
A
账户上的钱减少了100
元,但是第二步执行失败或者未执行便发生系统崩溃,导致B
账户并没有相应增加100
元 - 转账操作刚完成就发生系统崩溃,系统重启恢复时丢失了崩溃前的转账记录
- 同时又另一个用户转账给
B
账户,由于同时对B
账户进行操作,导致B
账户金额出现异常
为了便于解决这些问题,需要引入数据库事务的概念
1.3. 数据库事务的特性
- 原子性(
Atomicity
):事务中的所有操作作为一个整体像原子一样不可分割,要么全部成功,要么全部失败 - 一致性(
Consistency
):事务的执行结果必须使数据库从一个一致性状态到另一个一致性状态 - 隔离性(
Isolation
):并发执行的事务不会相互影响,其对数据库的影响和它们串行执行时一样。比如多个用户同时往一个账户转账,最后账户的结果应该和他们按先后次序转账的结果一样 - 持久性(
Durability
):事务一旦提交,其对数据库的更新就是持久的。任何事务或系统故障都不会导致数据丢失
按照严格的标准,只有同时满足 ACID
特性才是事务;但是在各大数据库厂商的实现中,真正满足 ACID
的事务少之又少。例如 MySQL
的 NDB Cluster
事务不满足持久性和隔离性;InnoDB
默认事务隔离级别是可重复读,不满足隔离性;Oracle
默认的事务隔离级别为 READ COMMITTED
,不满足隔离性……因此与其说 ACID
是事务必须满足的条件,不如说它们是衡量事务的四个维度
2. 数据库事务特性的实现原理
在说明原子性原理之前,首先介绍一下 MySQL
的事务日志。MySQL
的日志有很多种,如二进制日志、错误日志、查询日志、慢查询日志等,此外 InnoDB
存储引擎还提供了两种事务日志:redo log
([riːˈduː]
重做日志)和 undo log
([ʌnˈduː]
回滚日志)。其中 redo log
用于保证事务持久性;undo log
则是事务原子性和隔离性实现的基础
2.1. 原子性
undo log
是实现原子性的关键,当事务回滚时能够撤销所有已经成功执行的 SQL
语句。InnoDB
实现回滚,靠的就是 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.2. 持久性
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
宕机而丢失,从而满足了持久性要求
既然 redo log
也需要在事务提交时将日志写入磁盘,为什么它比直接将 Buffer Pool
中修改的数据写入磁盘(即刷脏)要快呢?
- 刷脏是随机
IO
,因为每次修改的数据位置随机,但写redo log
是追加操作,属于顺序IO
- 刷脏是以数据页为单位的,
MySQL
默认页大小是16KB
,一个数据页上一个小修改都要整页写入;而redo log
中只包含真正需要写入的部分,无效IO
大大减少
redo log
与 bin log
在 MySQL
中还存在 bin log
(二进制日志)也可以记录写操作并用于数据的恢复,但二者是有着根本的不同的
- 作用不同:
redo log
是用于事故恢复的,保证MySQL
宕机也不会影响持久性;bin log
是用于时间点恢复的,保证服务器可以基于时间点恢复数据,此外bin log
还用于主从复制 - 层次不同:
redo log
是InnoDB
存储引擎实现的,而bin log
是MySQL
的服务器层实现的,同时支持InnoDB
和其他存储引擎 - 写入时机不同:
bin log
在事务提交时写入;redo log
的写入时机相对多元
2.3. 隔离性
与原子性、持久性侧重于研究事务本身不同,隔离性研究的是不同事务之间的相互影响。隔离性追求的是并发情形下事务之间互不干扰。简单起见,我们主要考虑最简单的读操作和写操作(加锁读等特殊读操作会特殊说明),那么隔离性的探讨,主要可以分为两个方面
- 一个事务写操作对另一个事务写操作的影响:锁机制保证隔离性
- 一个事务写操作对另一个事务读操作的影响:
MVCC
保证隔离性
2.3.1. 锁机制
隔离性要求同一时刻只能有一个事务对数据进行写操作,InnoDB
通过锁机制来保证这一点。锁机制的基本原理可以概括为:事务在修改数据之前,需要先获得相应的锁;获得锁之后,事务便可以修改数据;该事务操作期间,这部分数据是锁定的,其他事务如果需要修改数据,需要等待当前事务提交或回滚后释放锁
MySQL
锁的分类
- 按锁的粒度划分,可分为表级锁、行级锁
- 按锁级别划分,可分为共享锁、排他锁
- 按使用方式划分,可分为乐观锁、悲观锁
本文重点是 MySQL
事务的实现原理,因此对锁的介绍到此为止,详情可参考 这里
2.3.2. 脏读、不可重复读和幻读
数据库在并发情况下,读操作可能存在的三类问题
- 脏读:当前事务
A
可以读到其他事务B
未提交的数据(脏数据),这种现象是脏读
- 不可重复读:在事务
A
先后两次读取同一个数据,两次读取的结果不一样,这种现象称为不可重复读。脏读与不可重复读的区别在于:前者读到的是其他事务未提交的数据,后者读到的是其他事务已提交的数据
- 幻读:在事务
A
按照某个条件先后两次查询数据库,两次查询结果的条数不同,这种现象称为幻读。不可重复读与幻读的区别可以通俗的理解为:前者是数据变了,后者是数据的行数变了
2.3.3. 数据库事务隔离级别
一般来说,隔离级别越低,系统开销越低,可支持的并发越高,但隔离性也越差。隔离级别与读问题的关系如下
事务隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
读未提交 | 会 | 会 | 会 |
读已提交 | 不会 | 会 | 会 |
可重复读 | 不会 | 不会 | 会 |
串行化 | 不会 | 不会 | 不会 |
在实际应用中,读未提交在并发时会导致很多问题,而性能相对于其他隔离级别提高却很有限,因此使用较少。可串行化强制事务串行,并发效率很低,只有当对数据一致性要求极高且可以接受没有并发时使用,因此使用也较少。因此在大多数数据库系统中,默认的隔离级别是读已提交(如 Oracle
)或可重复读(如 MySQL
)
2.3.4. MVCC
MVCC
全称 Multi-Version Concurrency Control
,即多版本的并发控制协议。下面的例子很好的体现了 MVCC
的特点:在同一时刻,不同的事务读取到的数据可能是不同的(即多版本)——在 T5
时刻,事务 A
和事务 C
可以读取到不同版本的数据
MVCC
最大的优点是读不加锁,因此读写不冲突,并发性能好。InnoDB
实现 MVCC
,多个版本的数据可以共存。下面以可重复读隔离级别为例,结合前文提到的几个问题分别说明
- 脏读
当事务 A
在 T3
时刻读取 zhangsan
的余额前,会生成 ReadView
,由于此时事务 B
没有提交仍然活跃,因此其事务 id
一定在 ReadView
的 rw_trx_ids
中,因此根据前面介绍的规则,事务 B
的修改对 ReadView
不可见。接下来,事务 A
根据指针指向的 undo log
查询上一版本的数据,得到 zhangsan
的余额为 100
。这样事务 A
就避免了脏读
- 不可重复读
当事务 A
在 T2
时刻读取 zhangsan
的余额前,会生成 ReadView
。此时事务 B
分两种情况讨论,一种是如图中所示,事务已经开始但没有提交,此时其事务 id
在 ReadView
的 rw_trx_ids
中;一种是事务 B
还没有开始,此时其事务 id
大于等于 ReadView
的 low_limit_id
。无论是哪种情况,根据前面介绍的规则,事务 B
的修改对 ReadView
都不可见
当事务 A
在 T5
时刻再次读取 zhangsan
的余额时,会根据 T2
时刻生成的 ReadView
对数据的可见性进行判断,从而判断出事务 B
的修改不可见;因此事务 A
根据指针指向的 undo log
查询上一版本的数据,得到 zhangsan
的余额为 100
,从而避免了不可重复读
- 幻读
MVCC
避免幻读的机制与避免不可重复读非常类似
2.3.5. 小结
InnoDB
实现的可重复读,通过锁机制、MVCC
等,实现了一定程度的隔离性,可以满足大多数场景的需要;但是,可重复读虽然避免了幻读问题,但是毕竟不是 Serializable
(串行化),不能保证完全的隔离
2.4. 一致性
可以说,一致性是事务追求的最终目标:前面提到的原子性、持久性和隔离性,都是为了保证数据库状态的一致性。此外,除了数据库层面的保障,一致性的实现也需要应用层面进行保障
- 保证原子性、持久性和隔离性,如果这些特性无法保证,事务的一致性也无法保证
- 数据库本身提供保障,例如不允许向整形列插入字符串值、字符串长度不能超过列的限制等
- 应用层面进行保障,例如如果转账操作只扣除转账者的余额,而没有增加接收者的余额,无论数据库实现的多么完美,也无法保证状态的一致
3. 总结
下面总结一下 ACID
特性及其实现原理
- 原子性:语句要么全执行,要么全不执行,是事务最核心的特性,事务本身就是以原子性来定义的;实现主要基于
undo log
- 持久性:保证事务提交后不会因为宕机等原因导致数据丢失;实现主要基于
redo log
- 隔离性:保证事务执行尽可能不受其他事务影响;
InnoDB
默认的隔离级别是可重复读,它的实现主要基于锁机制、MVCC
- 一致性:事务追求的最终目标,一致性的实现既需要数据库层面的保障,也需要应用层面的保障
数据库事务的特性及其实现原理相关推荐
- mysql数据库事务四大特性的实现原理
事务的四大特性 原子性.一致性.隔离性.持久性 原子性实现 原子性保证事务要么全执行成功,要么全不执行. mysql使用回滚机制实现,undo log实现回滚. 事务执行 insert.update. ...
- 数据库事务 四大特性
数据库事务四大特性(ACID) 敲黑板,这是重点,记下来!!会考到 转载连接:https://www.cnblogs.com/fjdingsd/p/5273008.html ⑴ 原子性(Atomici ...
- 数据库事务的概念及其实现原理
目录 1. 认识事务 1.1 为什么需要数据库事务 1.2 什么是数据库事务 1.3 事务如何解决问题 1.4 事务的ACID特性以及实现原理概述 2.并发异常与并发控制技术 2.1 常见的并发异常 ...
- 数据库 事务的特性ACID
事务(Transaction)是并发控制的基本单位. 所谓事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位.例如,银行转帐工作:从一个帐号扣款并使另一个帐号增款,这 ...
- [转]数据库事务ACID特性
ACID特性 数据库管理系统中事务(transaction)的四个特性(分析时根据首字母缩写依次解释):原子性(Atomicity).一致性(Consistency).隔离性(Isolation).持 ...
- mysql数据库事务传播特性_什么是事务的传播特性?
我们都知道事务的概念,那么事务的传播特性是什么呢?(此处着重介绍传播特性的概念,关于传播特性的相关配置就不介绍了,可以查看spring的官方文档) 在我们用SSH开发项目的时候,我们一般都是将事务设置 ...
- mysql事务的四大特性_浅谈数据库事务四大特性
数据库四大特性分别是:原子性.一致性.分离性.持久性.下面我们看看具体介绍. 原子性 事务的原子性指的是,事务中包含的程序作为数据库的逻辑工作单位,它所做的对数据修改操作要么全部执行,要么完全不执行. ...
- 数据库事务ACID特性
ACID特性 数据库管理系统中事务(transaction)的四个特性(分析时根据首字母缩写依次解释):原子性(Atomicity).一致性(Consistency).隔离性(Isolation).持 ...
- 数据库 事务 四大特性 原子性Atomic 一致性Consistent 隔离性Insulation Isolation 持久性Duration 隔离级别
https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E5%BA%93%E4%BA%8B%E5%8A%A1/9744607?fr=aladdin 数据库事务 ...
- 数据库事务ACID特性分析
在日常生活中,事务是无时无刻不存在的,什么是事务,按我的理解事务便是执行一段连续的不可分离的操作,这段操作是为了实现某一个目的而执行的,它不存在中间状态,要么执行成功,则该操作所带来的影响永久存在,要 ...
最新文章
- oracle基本命令集锦
- 【完结】16篇图像分类干货文章总结,从理论到实践全流程大盘点!
- Win10如何打开软键盘?
- 雪花算法之唯一ID生成器理解
- 使用 SAP Cloud SDK 开发应用时,如何通过环境变量的配置避免硬编码
- html二级页面内容滑动,jQuery+CSS实现的网页二级下滑菜单效果
- P4316-绿豆蛙的归宿【数学期望】
- [vim]在vim中格式化xml
- oracle segment undo_71_UNDO扩展学习
- 【Java】函数式接口与Lambda表达式
- 转载:bug的处理流程
- snmpset对象不可写_别再问了,好吗?Java字符串一定是不可变的
- 2012 ServerStandardEval 激活
- 医疗信息管理系统(HIS)——>业务介绍
- 封堵高危端口,预防勒索病毒
- URLConnection 传入参数
- 王小川告别搜狗那一天
- java 操作日志 log
- 计算机学院文化长廊,计算机学院2019年寝室文化节动员大会顺利召开
- c语言程序有且只有一个什么函数,一个c程序有且仅有一个什么函数
热门文章
- TensorFlow by Google过拟合优化 Machine Learning Foundations: Ep #7 - Image augmentation and overfitting
- 架构师架构蓝图《UML精粹》 UML Distilled读后感
- 单片机用C语言锯齿波,试用c语言编写一个能输出锯齿波信号的单片机c51程序
- mysql单机三实例_Mysql单机多实例
- linux下python3 安装tkinter库
- 第五章平稳过程(1)
- 公式推导 11-27
- 凸优化第八章几何问题 8.4极值体积椭圆
- 【机器学习系列】变分推断第二讲:基于Mean Field的变分推断解法
- 用Theano学习Deep Learning(三):卷积神经网络