共计 2154 个字符,预计需要花费 6 分钟才能阅读完成。
咱们都晓得,事务具备 ACID 四个个性——原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)、持久性(Durability)。但你晓得 MySQL 是通过什么技术手段来实现的吗?
ACID 简介
先来简略回顾一下 ACID 的定义:
原子性 :事务作为一个整体被执行,蕴含在其中的对数据库的操作要么全副被执行,要么都不执行。
一致性 :事务开始前和事务完结后,数据库的完整性没有被毁坏。即写入的数据必须完全符合所有的预设束缚、触发器、级联回滚等。
隔离性 :多个事务并发执行时,一个事务的执行不应影响其余事务的执行。
持久性 :已被提交的事务对数据库的批改应该永恒保留在数据库中。即便零碎挂了,数据也不会丢。
咱们依照:持久性 -> 原子性 -> 隔离性 -> 一致性 的程序来探讨。
PS:本文基于 InnoDB
持久性
咱们晓得程序修改数据的时候,是先将数据从磁盘加载到内存,而后批改完再由内存写回磁盘。长久化其实就是将内存里的数据写入磁盘。因而,持久性的要害就在于如何保证数据能够由内存顺利写入磁盘。
咱们有以下几个计划:
计划一:
- 加载数据到内存
- 批改内存
- 而后写回磁盘
- 提交事务
计划二:
- 加载数据到内存
- 批改内存
- 提交事务
- 后盾写回磁盘
第一种计划,靠谱是靠谱,但性能太低,会重大连累 MySQL 的吞吐量。
第二种计划尽管性能上来了,但如果在第四步时宕机了,而零碎认为事务已提交,这时候就会失落数据了。
那怎么办呢?MySQL 给出的计划是 WAL(Write Ahead Log)机制。WAL 翻译过去就是先写日志的意思。这个日志就是 redo log。具体做法是:
- 加载数据到内存
- 批改内存
- 写入 redo log
- 提交事务
- 后盾写回磁盘
如果第五步时零碎宕机,也能够通过 redo log 来复原。
你可能有疑难:写入 redo log 不也有磁盘 I/O 吗?这不是脱了那啥再那啥,多此一举吗?写 redo log 和写表的区别就在于随机写和程序写。MySQL 的表数据是随机存储在磁盘中的,而 redo log 是一块固定大小的间断空间。而磁盘程序写入要比随机写入快几个数量级。
因而,这种计划即保障了数据的平安,性能上也可能承受。
原子性
如果一个事务做了如下操作:
- 插入一条数据 insert into user values(‘1′,’ 小刘 ’,’18’)
- 更新一条数据 update user set name = ‘ 小水 ’ where id = 2
- 删除一条数据 delete from user where id = 3
依据原子性的规定,这三个操作要么都胜利,要么都失败。那么问题就来了,如何保障 3 失败的状况下,让 1,2 也回退呢?
答案就是 undo log。
每个事务操作(增删改)都会记录一条与之对应的 undo log:
- insert 记录插入的主键,回滚则依据该主键删除记录
- update 记录记录主键和被批改列的以后值,回滚则依据主键和之前的值笼罩
- delete 为记录增加删除标记,即 MySQL 外部的逻辑删除,回滚依据主键复原
隔离性
数据库事务有四种隔离级别,不同的级别可能会呈现各种各样的问题(脏读、幻读、不可反复读),关系如下:
隔离级别 | 脏读 | 不可反复读 | 幻读 |
---|---|---|---|
读未提交(Read uncommitted) | 可能 | 可能 | 可能 |
读已提交(Read committed) | 不可能 | 可能 | 可能 |
可反复读(Repeatable read) | 不可能 | 不可能 | 可能 |
可串行化(Serializable) | 不可能 | 不可能 | 不可能 |
MySQL 中 RR 级别曾经解决了幻读问题。
并发的状况才须要隔离,而并发有三种组合:
- 读读
- 读写
- 写写
「读读」的状况,不须要隔离;「读写」通过 MVCC 隔离;「写写」只能通过锁来隔离。
MVCC(Multi Version Concurrency Control,多版本并发管制)作用于 RC 和 RR 级别。能够为事务中的读操作创立一个快照(Readview),从而来防止被其余事务烦扰。
RC 级别下,一个事务中的每次(同参数)读都会创立一个 Readview。
RR 级别下,一个事务中只在第一次读时创立 Readview,前面再次读,依然读取该 Readview。
「写写」的状况通过三种锁来实现隔离:Record Lock、Gap Lock 和 Next Key Lock(前两者的组合)。
Record Lock 锁住一条数据,从而使其余事务无奈批改和删除;Gap Lock 锁住一个范畴,从而使其余事务不能在该区间插入数据;Next Key Lock 锁住具体数据和区间,从而使其余事务无奈更新、删除和在该区间插入数据。
MVCC + 锁 使得 MySQL 在 RR 级别防止了幻读问题。
一致性
很多人聊到一致性,很喜爱拿转账的业务举例,但这显著是原子性的领域——A 账户扣钱,B 账户加钱,两个 Update 操作,要么都胜利,要么都失败。
一致性更偏重是,数据的完整性:主外键束缚、惟一索引、列残缺等。MySQL 中保障一致性次要靠 CR(Crash Recovery)和 DWB(Doublewrite Buffer)来保障的。
这两个个性比较复杂,一篇文章基本讲不完,如果你感兴趣能够去看官网文档,或者留言通知我,我来安顿。
最初
一致性是一个比拟非凡的存在,它和原子性、隔离性有一层「你中有我,我中有你」的暧昧关系。比方转账的业务场景,如果说它属于一致性的领域,也可能说得通,能够叫「用户自定义一致性」;另外,隔离性使得事务之间互不影响的最终成果也是保障了数据的统一。