MySQL探秘(八):InnoDB的事务
来源:SegmentFault
时间:2023-02-24 17:30:01 254浏览 收藏
哈喽!今天心血来潮给大家带来了《MySQL探秘(八):InnoDB的事务》,想必大家应该对数据库都不陌生吧,那么阅读本文就都不会很困难,以下内容主要涉及到MySQL、事务、InnoDB,若是你正在学习数据库,千万别错过这篇文章~希望能帮助到你!
事务是数据库最为重要的机制之一,凡是使用过数据库的人,都了解数据库的事务机制,也对ACID四个基本特性如数家珍。但是聊起事务或者ACID的底层实现原理,往往言之不详,不明所以。所以,今天我们就一起来分析和探讨InnoDB的事务机制,希望能建立起对事务底层实现原理的具体了解。
数据库事务具有ACID四大特性。ACID是以下4个词的缩写:
- 原子性(atomicity) :事务最小工作单元,要么全成功,要么全失败 。
- 一致性(consistency): 事务开始和结束后,数据库的完整性不会被破坏 。
- 隔离性(isolation) :不同事务之间互不影响,四种隔离级别为RU(读未提交)、RC(读已提交)、RR(可重复读)、SERIALIZABLE (串行化)。
- 持久性(durability) :事务提交后,对数据的修改是永久性的,即使系统故障也不会丢失 。
下面,我们就以一个具体实例来介绍数据库事务的原理,并介绍InnoDB是如何实现ACID四大特性的。
示例介绍
我们首先来看一下具体的示例。大家可以自己亲自试验一下,这样理解和记忆都会更加深刻。
首先,使用如下的SQL语句创建两张表,分别是goods和trade,代表货物和交易。并向goods表中插入一条记录,id为1的货物数量为10。
CREATE TABLE goods (id INT, num INT, PRIMARY KEY(id)); CREATE TABLE trade (id INT, goods_id INT, user_id INT, PRIMARY KEY(id)); INSERT INTO goods VALUES(1, 10);
然后打开终端,连接数据库,开启会话一,先用BEGIN显示开启一个事务。会话一先将goods表中id为1的货物的数量减一,然后向trade表中添加一笔交易的记录,最后使用COMMIT显示提交事务。
而会话二则先查询goods表中id为1的货物数量,然后向trade表中添加一笔交易记录,接着更新goods表中id为1的货物的数量,最后使用ROLLBACK进行事务的回滚。其中,两个会话中执行的具体语句和先后顺序如下图所示。
这个示例可以体现数据库事务的很多特性,我们一一来介绍。首先会话一的操作2更新了id为1的货物的数量,但是会话二的操作5读出来的数量仍然是10,这体现了事务的隔离性,使用InnoDB的多版本控制机制实现。
会话二的操作7也要更新同种货物的数量,此时因为会话一的操作2已经更新了该货物的数量,InnoDB已经锁住了该记录的行锁,所以操作7会被阻塞,直到会话一COMMIT。但是会话一的操作4和会话二的操作7都是向trade表中插入记录,后者却不会因为前者而阻塞,因为二者插入的不是同一行记录。锁机制是一种常见的并发控制机制,它和多版本控制机制一起实现了InnoDB事务的隔离性,关于InnoDB锁相关的具体内容可以参考InnoDB锁的类型和状态查询和InnoDB行锁算法。
会话一事务最终使用COMMIT提交了事务而会话二事务则使用ROLLBACK回滚了整个事务,这体现了事务的原子性。即事务的一系列操作要么全部执行(COMMIT),要么就全部不执行(ROLLBACK),不存在只执行一部分的情况。InnoDB使用事务日志系统来实现事务的原子性。这里有的同学就会问了,如果中途连接断开或者Server Crash会怎么样。能怎么样,直接自动回滚呗。
一旦会话一使用COMMIT操作提交事务成功后,那么数据一定会被写入到数据库中并持久的存储起来,这体现了事务的持久性。InnoDB使用redo log机制来实现事务的持久性。
而事务的一致性比较难以理解,简单的讲在事务开始时,此时数据库有一种状态,这个状态是所有的MySQL对象处于一致的状态,例如数据库完整性约束正确,日志状态一致等。当事务提交后,这时数据库又有了一个新的状态,不同的数据,不同的索引,不同的日志等。但此时,约束,数据,索引,日志等MySQL各种状态还是要保持一致性。 也就是说数据库从一个一致性的状态,变到另一个一致性的状态。事务执行后,并没有破坏数据库的完整性约束。
下面我们就来详细讲解一下上述示例涉及的事务的ACID特性的具体实现原理。总结来说,事务的隔离性由多版本控制机制和锁实现,而原子性、一致性和持久性通过InnoDB的redo log、undo log和Force Log at Commit机制来实现。
原子性,持久性和一致性
原子性,持久性和一致性主要是通过redo log、undo log和Force Log at Commit机制机制来完成的。redo log用于在崩溃时恢复数据,undo log用于对事务的影响进行撤销,也可以用于多版本控制。而Force Log at Commit机制保证事务提交后redo log日志都已经持久化。
开启一个事务后,用户可以使用COMMIT来提交,也可以用ROLLBACK来回滚。其中COMMIT或者ROLLBACK执行成功之后,数据一定是会被全部保存或者全部回滚到最初状态的,这也体现了事务的原子性。但是也会有很多的异常情况,比如说事务执行中途连接断开,或者是执行COMMIT或者ROLLBACK时发生错误,Server Crash等,此时数据库会自动进行回滚或者重启之后进行恢复。
我们先来看一下redo log的原理,redo log顾名思义,就是重做日志,每次数据库的SQL操作导致的数据变化它都会记录一下,具体来说,redo log是物理日志,记录的是数据库页的物理修改操作。如果数据发生了丢失,数据库可以根据redo log进行数据恢复。
InnoDB通过Force Log at Commit机制实现事务的持久性,即当事务COMMIT时,必须先将该事务的所有日志都写入到redo log文件进行持久化之后,COMMIT操作才算完成。
当事务的各种SQL操作执行时,即会在缓冲区中修改数据,也会将对应的redo log写入它所属的缓存。当事务执行COMMIT时,与该事务相关的redo log缓冲必须都全部刷新到磁盘中之后COMMIT才算执行成功。
redo log写入磁盘时,必须进行一次操作系统的fsync操作,防止redo log只是写入了操作系统的磁盘缓存中。参数innodb_flush_log_at_trx_commit可以控制redo log日志刷新到磁盘的策略,它的具体作用可以查阅InnoDB的磁盘文件及落盘机制
redo log全部写入磁盘后事务就算COMMIT成功了,但是此时事务修改的数据还在内存的缓冲区中,称其为脏页,这些数据会依据检查点(CheckPoint)机制择时刷新到磁盘中,然后删除相应的redo log,但是如果在这个过程中数据库Crash了,那么数据库重启时,会依据redo log file将那些还在内存中未更新到磁盘上的数据进行恢复。
数据库为了提高性能,数据页在内存修改后并不是每次都会刷到磁盘上。而是引入checkpoint机制,择时将数据页落盘,checkpoint记录之前的数据页保证一定落盘了,这样相关的redo log就没有用了(由于InnoDB redo log file循环使用,这时这部分日志就可以被覆盖),checkpoint之后的数据页有可能落盘,也有可能没有落盘,所以checkpoint之后的redo log file在崩溃恢复的时候还是需要被使用的。InnoDB会依据脏页的刷新情况,定期推进checkpoint,从而减少数据库崩溃恢复的时间。检查点的信息在第一个日志文件的头部。
数据库崩溃重启后需要从redo log中把未落盘的脏页数据恢复出来,重新写入磁盘,保证用户的数据不丢失。当然,在崩溃恢复中还需要回滚没有提交的事务。由于回滚操作需要undo日志的支持,undo日志的完整性和可靠性需要redo日志来保证,所以崩溃恢复先做redo恢复数据,然后做undo回滚。
在事务执行的过程中,除了记录redo log,还会记录一定量的undo log。undo log记录了数据在每个操作前的状态,如果事务执行过程中需要回滚,就可以根据undo log进行回滚操作。
undo log的存储不同于redo log,它存放在数据库内部的一个特殊的段(segment)中,这个段称为回滚段。回滚段位于共享表空间中。undo段中的以undo page为更小的组织单位。undo page和存储数据库数据和索引的页类似。因为redo log是物理日志,记录的是数据库页的物理修改操作。所以undo log的写入也会产生redo log,也就是undo log的产生会伴随着redo log的产生,这是因为undo log也需要持久性的保护。如上图所示,表空间中有回滚段和叶节点段和非叶节点段,而三者都有对应的页结构。
我们再来总结一下数据库事务的整个流程,如下图所示。
事务进行过程中,每次sql语句执行,都会记录undo log和redo log,然后更新数据形成脏页,然后redo log按照时间或者空间等条件进行落盘,undo log和脏页按照checkpoint进行落盘,落盘后相应的redo log就可以删除了。此时,事务还未COMMIT,如果发生崩溃,则首先检查checkpoint记录,使用相应的redo log进行数据和undo log的恢复,然后查看undo log的状态发现事务尚未提交,然后就使用undo log进行事务回滚。事务执行COMMIT操作时,会将本事务相关的所有redo log都进行落盘,只有所有redo log落盘成功,才算COMMIT成功。然后内存中的数据脏页继续按照checkpoint进行落盘。如果此时发生了崩溃,则只使用redo log恢复数据。
隔离性
InnoDB事务的隔离性主要通过多版本控制机制和锁机制实现,具体可以参考多版本控制,InnoDB锁的类型和状态查询和InnoDB行锁算法三篇文章。
后记
本来想一篇文章将MySQL的事务机制讲明白,写完自己读了一遍,还是发现内容有些晦涩难懂,复杂的知识本来就是很难讲明白的,夫夷以近,则游者众;险以远,则至者少,希望读者以本文作为一篇指引性的文章,自己再去更加深入的地方去探秘。不过,能将复杂知识讲解的通俗简单也是一项很大的本领,文字和讲解能力还是需要提示的。
- Mysql探索(一):B-Tree索引
- 数据库内部存储结构探索
- MySQL探秘(二):SQL语句执行过程详解
- MySQL探秘(三):InnoDB的内存结构和特性
- MySQL探秘(四):InnoDB的磁盘文件及落盘机制
- MySQL探秘(五):InnoDB锁的类型和状态查询
- MySQL探秘(六):InnoDB一致性非锁定读
参考
文中关于mysql的知识介绍,希望对你的学习有所帮助!若是受益匪浅,那就动动鼠标收藏这篇《MySQL探秘(八):InnoDB的事务》文章吧,也可关注golang学习网公众号了解相关技术文章。
-
499 收藏
-
244 收藏
-
235 收藏
-
157 收藏
-
101 收藏
-
475 收藏
-
266 收藏
-
273 收藏
-
283 收藏
-
210 收藏
-
371 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习
-
- 柔弱的水杯
- 感谢大佬分享,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢楼主分享技术文章!
- 2023-05-19 02:34:25
-
- 甜美的凉面
- 这篇技术文章出现的刚刚好,细节满满,写的不错,已收藏,关注up主了!希望up主能多写数据库相关的文章。
- 2023-03-25 20:56:04
-
- 犹豫的耳机
- 这篇技术贴出现的刚刚好,太详细了,赞 👍👍,已加入收藏夹了,关注老哥了!希望老哥能多写数据库相关的文章。
- 2023-03-25 17:43:49
-
- 坚定的网络
- 这篇文章内容太及时了,细节满满,赞 👍👍,mark,关注楼主了!希望楼主能多写数据库相关的文章。
- 2023-03-17 10:01:56
-
- 激动的黑夜
- 很详细,已收藏,感谢作者的这篇文章内容,我会继续支持!
- 2023-03-04 13:43:37
-
- 典雅的镜子
- 这篇文章太及时了,太细致了,受益颇多,mark,关注老哥了!希望老哥能多写数据库相关的文章。
- 2023-03-02 14:40:17
-
- 靓丽的戒指
- 很好,一直没懂这个问题,但其实工作中常常有遇到...不过今天到这,看完之后很有帮助,总算是懂了,感谢老哥分享文章!
- 2023-03-01 07:29:34
-
- 顺心的星星
- 这篇文章内容太及时了,博主加油!
- 2023-02-26 05:49:25