(2022.04.24 Sun)
事务Transaction,是用于从数据库获取数据或修改数据的一个可以独立执行的逻辑单元。这个逻辑单元是对数据库执行操作的一个/多个命令构成的操作序列。
事务的特性 - ACID
ACID代表了事务的四个属性,Atomicity原子性,Consistency连贯性,Isolation隔离性,Durability持久性。一个常见的事务案例是从A账户向B账户转账100元。
-
Atomicity
原子性表明一个事务在执行过程中要么全部完成(committed),要么没有任何一部分完成(rollback)。比如上面提到的案例,该事务包括两步,分别是A账户减少100元和B账户增加100元。该事务要么两步都成功执行,要么哪一步都不执行 -
Consistency
数据库在事务执行之前和之后,状态保持一致,约束不被破坏。比如事务案例的余额字段保存的是一个浮点数字,在事务的执行过程中该字段不能设为字符型。A的账户减少和B的账户增加应一致。 -
Isolation
对同一数据库并行操作的多个事务,彼此之间应该不互相影响。有的数据库会在事务并行执行时加锁机制,以确保不同的事务之间彼此不影响。数据库通过设置隔离级别(isolation level)以确保事务完整性(transaction integrity)的程度。 -
Durability
事务在提交后对数据库的改变是永久性的。在事务执行之后,数据库的事务日志(transaction log)中会针对成功运行的事务添加一条记录以确保事务的持久性。
(2022.04.25 Mon)
事务的状态
- 活跃(active state):事务执行了读/写操作之后就进入活跃态
- 部分提交(partial committed):状态已经在活跃的基础发生改变,但是数据库(的硬盘)还没有得到提交的变化,此时修改保存在内存缓冲(memory buffer)中
- 提交(committed):事务所有的更新已经写入数据库,因此回滚(rollback)操作在这个状态之后已不可能
- 失败(failed):事务执行失败,或从部分提交和活跃态中止(aborted),则进入失败状态
- 结束(terminated):事务提交或中止,则进入结束状态,事务的生命周期结束
ACID实现原理
大厂产品中能够真正满足ACID的数据库实现比较稀有。如MySQL 的NDB Cluster事务不满足持久性和隔离性;InnoDB 默认事务隔离级别是可重复读,不满足隔离性;Oracle 默认的事务隔离级别为 READ COMMITTED,不满足隔离性等等。这里以MySQL的InnoDB为例,简要介绍ACID的实现。
MySQL中,
A原子性由undo log实现;
C一致性由redo/undo log和并发控制技术MCVV实现;
I隔离性由读写锁MCVV实现;
D持久性由redo log实现。
MySQL中的事务日志有两种,分别是undo log和redo log。
在介绍原理前,先来浏览一遍InnoDB写入数据的过程。
InnoDB更新数据库的过程
InnoDB作为MySQL的存储引擎,数据存放在磁盘中.如果每次读写数据都需要磁盘IO,效率会很低。为此,InnoDB提供了缓存(Buffer Pool),Buffer Pool中包含了磁盘中部分数据页的映射,作为访问数据库的缓冲:当从数据库读取数据时,会首先从Buffer Pool中读取,如果Buffer Pool中没有,则从磁盘读取后放入Buffer Pool;当向数据库写入数据时,会首先写入Buffer Pool,Buffer Pool中修改的数据会定期刷新到磁盘中,即刷脏。Buffer Pool的使用大大提高了读写数据的效率,但是也带了新的问题:如果MySQL宕机,而此时Buffer Pool中修改的数据还没有刷新到磁盘,就会导致数据的丢失,事务的持久性无法保证。
undo log
undo log属于逻辑日志,用于保证事务的原子性;记录着SQL执行的反向信息。当事务回滚(rollback)时,InnoDB执行undo log中的动作。下面是执行动作对比
action in transaction | action in undo log&rollback |
---|---|
insert | delete |
delete | insert |
update | ~update |
简言之,回滚操作的目的是当事务执行失败或abort,可按照undo log中的信息返回到事务之前的样子。
redo log
redo log用来实现D持久性,由两部分组成,重做日志缓冲(redo log buffer)以及重做日志文件(redo log),前者是在内存中,后者在数据磁盘上。
当数据修改时,除了修改Buffer Pool中的数据,还会在redo log Buffer 中记录这次操作;当事务提交时,会将redo log写入硬盘。如果MySQL宕机,重启时可以读取redo log中的数据,对数据库进行恢复。redo log采用的是WAL,i.e., Write-ahead logging预写式日志,所有修改先写入日志,再更新到Buffer Pool,进而写入数据库所在的硬盘页,当系统崩溃时,虽然数据没有持久化,但是redo log已经持久化。系统可以根据redo log的内容,将所有数据恢复到latest状态。保证了数据不会因MySQL宕机而丢失,从而满足了持久性要求。
Reference
1 cnblogs - superChong
2 知乎 - MySQL的undo log,连边
3 百度 - 深入了解MySQL数据库事务原理四大特性,Java架构大仙
4 segmentfault - 数据库事务的实现原理,KerryWu
5 百度 - 详解MySQL事务原理,老刘情感影视
网友评论