“ ACID 事务底层内部实现原理”
Hello,大家好。我是公众号“八点半技术站”的小编-Bruce.D。
今天是周二(2020-03-24),还是那句俗语 “一日之计在于晨” ,因此分享给大家的是 「mysql 模块」- ACID 内部实现原理。想必在 mysql 中,事务大家一定不陌生,但是我估计了解最多的是它 ACID 的表名特性含义吧。事务,我们想要达到什么结果呢?无非不就是 可靠性 与 并发 处理。
今天就让我来为大家进行 ACID 底层内部 “解刨” 分析。
01
—
ACID 概述
所谓事务是用户自定义的一个数据库操作序列,这些操作是一个不可分割的单位。我们要知道,事务 和 程序 是两个概念,一般来讲,一个程序中包含多个事务。
事务的 开始与结束 可以由用户来控制。如果用户没有显示的定义事务,则由数据库管理系统按默认规定自动划分事务。在 SQL 中,定义事务的语句一般有三条:
(1)BEGIN TRANSACTION
(2)COMMIT
(3)ROLLBACK
事务通常是以BEGIN TRANSACTION 开始,以 COMMIT 或 ROLLBACK 结束。COMMIT 表示提交,即提交事务的所有操作。具体的说就是将事务中的所有对数据库的更新写回到磁盘上的物理数据库中,事务正常结束。ROLLBACK表示回滚,即在事务中运行的过程中发生了某种故障,事务不能继续执行,系统将事务中对数据库所有已完成的操作全部撤销,回滚到事务开始时的状态,这里的操作指对数据库的更新操作。
02
—
ACID 特性
A(Atomicity):原子性
原子性是指事务是一个不可分割的工作单位,事务中操作要么都发生,要么都不发生。
C(Consistency):一致性
事务前后的数据必须保持一致性。
I(Isolation):隔离性
事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务所干扰,多个并发事务之间要相互隔离。
D(Durability):持久性
持久性是指一个事务一旦被提交,它对数据库中的数据改变是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。
03
—
事务的实现
在开始讲解最重要的一块技术点之前,我先给大家简单简述一下,方便大家在浏览的时候,结合我的文章 与 自己的思路 更好梳理。(不懂的一定要耐心浏览,细节很重要)
我们首先要知道 重做日志、回滚日志以及锁技术就是实现事务的基础。
事务的原子性是通过undolog来实现的。
事务的持久性性是通过redolog来实现的。
事务的隔离性是通过(读写锁+MVCC)来实现的。
事务的一致性是通过原子性,持久性,隔离性来实现的!!!
原子性、持久性、隔离性 最终目的也是为了保障数据的一致性。
第一小节:原子性的实现
首先说一下,所谓回滚操作就是当发生错误异常或者显示的执行rollback语句时候,需要把数据还原到原先的模样。所以这时候也就需要用到 undo log 来进行回滚,接下来看一下 undo log 在实现事务原子性时怎么发挥作用的。
undo log 生成:
(1)每条数据变更(insert/update/delete)操作都伴随一跳undo log的生成,并且回滚日志必须先于数据持久化到磁盘。
(2)所谓的回滚就是根据回滚日志做逆向操作,比如 delete 的逆向操作为 insert ,insert的逆向操作为delete,update的逆向为update等。
根据 undo log 进行回滚 :
为了做到同时成功或者失败,当系统发生错误或者执行 rollback 操作时需要根据 undo log 进行回滚。
回滚操作就是要还原到原来的状态,undo log记录了数据被修改前的信息以及新增和被删除的数据信息,根据undo log生成回滚语句,比如:
(1) 如果在回滚日志里有新增数据记录,则生成删除该条的语句
(2) 如果在回滚日志里有删除数据记录,则生成生成该条的语句
(3) 如果在回滚日志里有修改数据记录,则生成修改到原先数据的语句
第二小节:持久性的实现
先了解一下MySQL的数据存储机制,MySQL的表数据是存放在磁盘上的,因此想要存取的时候都要经历磁盘 IO,然而即使是使用 SSD 磁盘 IO 也是非常消耗性能的。为此,为了提升性能 InnoDB 提供了缓冲池(Buffer Pool),Buffer Pool 中包含了磁盘数据页的映射,可以当做缓存来使用:
读数据:会首先从缓冲池中读取,如果缓冲池中没有,则从磁盘读取在放入缓冲池;
写数据:会首先写入缓冲池,缓冲池中的数据会定期同步到磁盘中;
上面这种缓冲池的措施虽然在性能方面带来了质的飞跃,但是它也带来了新的问题,当MySQL系统宕机,断电的时候可能会丢数据!!!
因为我们的数据已经提交了,但此时是在缓冲池里头,还没来得及在磁盘持久化,所以我们急需一种机制需要存一下已提交事务的数据,为恢复数据使用。
于是 redo log就派上用场了。
既然redo log也需要存储,也涉及磁盘IO为啥还用它?
(1)redo log 的存储是顺序存储,而缓存同步是随机操作。
(2)缓存同步是以数据页为单位的,每次传输的数据大小大于redo log。
第三小节:隔离性的实现
隔离性是事务ACID特性里最复杂的一个。在SQL标准里定义了四种隔离级别,每一种级别都规定一个事务中的修改,哪些是事务之间可见的,哪些是不可见的。
级别越低的隔离级别可以执行越高的并发,但同时实现复杂度以及开销也越大。
Mysql 隔离级别有以下四种(级别由低到高):
READUNCOMMITED(未提交读)
READCOMMITED(提交读)
REPEATABLEREAD(可重复读)
SERIALIZABLE (可重复读)
只要彻底理解了隔离级别以及他的实现原理就相当于理解了ACID里的隔离型。前面说过原子性,隔离性,持久性的目的都是为了要做到一致性,但隔离型跟其他两个有所区别,原子性和持久性是为了要实现数据的可性保障靠,比如要做到宕机后的恢复,以及错误后的回滚。
那么隔离性是要做到什么呢? 隔离性是要管理多个并发读写请求的访问顺序。 这种顺序包括串行或者是并行
说明一点,写请求不仅仅是指insert操作,又包括update操作。
总之,从隔离性的实现可以看出这是一场数据的可靠性与性能之间的权衡。
第四小节:一致性的实现
数据库总是从一个一致性的状态转移到另一个一致性的状态。
举个例:小盟从银行卡转 400 到 基金账户
1.假如执行完 update bank set balance = balance - 400;之发生异常了,银行卡的钱也不能平白无辜的减少,而是回滚到最初状态。
2.又或者事务提交之后,缓冲池还没同步到磁盘的时候宕机了,这也是不能接受的,应该在重启的时候恢复并持久化。
3.假如有并发事务请求的时候也应该做好事务之间的可见性问题,避免造成脏读,不可重复读,幻读等。在涉及并发的情况下往往在性能和一致性之间做平衡,做一定的取舍,所以隔离性也是对一致性的一种破坏。
总结
实现事务采取了哪些技术以及思想?
★ 原子性:使用 undo log ,从而达到回滚
★ 持久性:使用 redo log,从而达到故障后恢复
★ 隔离性:使用锁以及MVCC,运用的优化思想有读写分离,读读并行,读写并行
★ 一致性:通过回滚,以及恢复,和在并发环境下的隔离做到一致性。
————————end————————
欢迎大家加入WeChat 技术交流群「wechat:xzzs730」
技术群交流 长按 ↑ 上方二维码 扫码加群
链接地址:技术群分享-存档记录
https://github.com/gtcarry888/WeChat-Sharing-records
如有其他问题,可留言至 gtcarry999@163.com 邮箱。