事务的ACID特性
参考:https://baijiahao.baidu.com/s?id=1625607423998953705&wfr=spider&for=pc
mysql逻辑架构图
- 第一层:处理客户端连接,授权认证等
- 第二层:服务器层,负责查询语句的解析、优化、缓存。
- 第三层:存储引擎,负责数据的存储和提取,事务由存储引擎实现的。
ACID(Atomicity原子性,Consistency一致性,Isolation隔离性,Durability持久性)是衡量事务的4个特性。原则上,只有同时满足ACID特性才是事务,但是在各大数据库厂商实现中,真正满足ACID的事务少之又少。
1 原子性
原子性是指一个事务是一个不可分割的工作单位,其中要么都做,要么都不做。
实现原理undo log。
InnoDB存储引擎提供了两种事务日志,redo log和undo log。其中redo log用于保证事务持久性,undo log保证事务原子性和隔离性。
InnoDB实现回滚,靠的是undo log:
- a当事务对数据库进行修改时,InnoDB会生成对应的undo log。
- b如果事务回滚,则利用undo log将数据回滚到修改之前的样子。
2 持久性
持久性是指,事务一旦提交,它对数据库的改变就应该是永久性的。
实现原理,redo log
InnoDB为了减少磁盘IO次数,提供了Buffer Pool(缓存),作为访问数据库的缓冲。
a当从数据库读取数据时,会首先从Buffer Pool中读取,如果Buffer Pool中没有,则从磁盘读取后放入Buffer Pool。
b当向数据库写入数据时,会首先写入Buffer Pool,Buffer Pool中修改的数据会定期刷新到磁盘中(这一过程称为刷脏)。
Buffer Pool大大提高了读写性能,但是带来了新的问题,如果mysql宕机,数据有丢失的风险,持久性无法保证。
因此,redo log被引来解决这个问题,当数据修改时,除了修改Buffer Pool中的数据,还会在redo log记录这次操作;当事务提交时,会调用fsync接口对redo log进行刷盘。
所有修改先写入redo log,再更新到Buffer Pool,保证了数据不会因宕机而丢失。从而满足了持久性的要求。
2.1 redo log和binlog
作用不同:redo log是用于crash recovery的,保证宕机也不会影响持久性。Binlog是用于point-in-time recovery的,保证服务器可以基于时间点恢复数据,此外binlog还用于主从复制。
层次不同:redo log是InnoDB存储引擎实现的;binlog是MySql服务器层实现的,同时支持InnoDB和其他存储引擎
内容不同:redo log是物理日志,内容基于磁盘的page;binlog是逻辑日志,内容是一条条sql。
写入时机不同:redo log的写入时机相对多元,默认情况下,当事务提交时会调用fsync对redo log进行刷盘。除了事务提交时,还有其他刷盘时机。Binlog在事务提交时写入。
3 隔离性
隔离性针对的是不同事务之间的相互影响。隔离性是指事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
严格的隔离性,对应了事务隔离级别中的可串行化,但实际应用中出于性能的考虑很少使用可串行化。
3.1 锁机制
隔离性要求同一时刻只能有一个事务对数据进行写操作,InnoDB通过锁机制来保证。
- a 事务在修改数据之前,需要先获得相应的锁。
- b 获得锁之后,事务便可以修改数据
- c 该事务操作期间,这部分数据是锁定的,其他事务必须等待。
InnoDB支持行锁和表锁。Myisam只支持表锁。
锁会消耗资源,因此在锁定数据较多的情况下,使用表锁可以节省大量资源。
我们可以通过如下命令查看InnoDB整体状态,其中就包含锁的情况。
mysql> show engine innodb status;
如下是模拟开启两个客户端,开启事务修改同一行数据,但不提交,查询InnoDB状态的信息
我们可以看到事务2581和2580占用锁的情况。
3.2 脏读
事务A读取到事务B未提交的数据。
3.3 不可重复读
事务A中先后两次读取同一数据,两次读取到的结果不一样。
和脏读的区别,脏读:事务A读到的是事务B未提交的数据。而不可重复读:事务A读取到事务B已提交的数据
3.4 幻读
在事务A中按照某个条件先后两次查询数据库,两次查询结果的条数不同。
3.5 事务的隔离级别
读未提交、读已提交、可重复读、可串行化
InnoDB默认的隔离级别:可重复读
3.6 MVCC
Multi-Version Concurrency Control,即多版本并发控制。
通过MVCC可以解决脏读、不可重复读、幻读问题。
MVCC最大的优点是读不加锁,读写不冲突,并发性能好。
InnoDB实现MVCC,使得多个版本的数据可以共存,其主要是依靠数据的隐藏列和undo log。
数据的隐藏列包含了该数据的版本号、删除时间、指向undo log的指针等等。当读数据时,InnoDB可以根据隐藏列判断是否需要回滚并找到回滚需要的undo log,从而实现MVCC。
4 一致性
一致性是事务追求的终极目标。
一致性是指事务执行结束后,数据库的完整性约束没有被破坏。
数据库的完整性约束:实体完整性、列完整性、外键约束、用户自定义完整性。
一致性需要数据库层面的保障和应用程序层面的保障。两者合力才能够尽量确保一致性。比如说如果转账操作只扣除转账者的余额,而没有增加接收者的余额,无论数据库实现有多完美,也无法保证状态的一致。