前言

大家都知道事务有四个特性:

  • 原子性(atomicity)

    原子性是指整个数据库事务是不可分割的工作单位。只有使事务中所有的数据库操作执行都成功,才算整个事务成功。如果事务中任何一个SQL语句执行失败,那么已经执行成功的SQL语句也必须撤销,数据库状态应该退回到执行事务前的状态。

  • 一致性(consistency)

    一致性指事务将数据库从一种状态转变为下一种一致的状态。在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。

  • 隔离性(isolation)

    一个事务的影响在该事务提交前对其他事务都不可见——这通过锁来实现。

  • 持久性(durability)

    事务一旦提交,其结果就是永久性的。即使发生宕机等故障,数据库也能将数据恢复。

对于隔离性的实现,可以参考我的这篇文章:InnoDB锁与事务简析

本篇文章主要讲述其它三个特性是如何实现的:通过数据库的redo和undo来完成。本文如果不做特别说明,默认指的是mysql的InnoDB存储引擎。

实现

基础知识

  1. redo log记录的关于每个页(Page)的更改的物理情况
  2. undo log和redo log记录物理日志不一样,它是逻辑日志。可以认为当delete一条记录时,undo log中会记录一条对应的insert记录,反之亦然,当update一条记录时,它记录一条对应相反的update记录。
  3. LSN称为日志的逻辑序列号(log sequence number),在innodb存储引擎中,lsn占用8个字节。LSN的值会随着日志的写入而逐渐增大。事务中更新操作会产生一个新的LSN。LSN不仅存在于redo log中,还存在于数据页中。
  4. checkpoint检查点,表示脏页写入到磁盘的时候

整体流程


无论是日志还是数据,都是现在buffer里修改,然后再同步到磁盘上的。

  1. 启动事务后,修改的数据如果没有在log buffer中,则会从磁盘读取到log buffer

  2. 对内存中数据修改之前,将原始数据记录到undo log

  3. 对内存中的数据页进行修改,在内存数据页中记录LSN,暂且称之为data_in_buffer_lsn

  4. 在修改数据页的同时(几乎是同时)向redo log in buffer中写入redo log,并记录下对应的LSN,暂且称之为redo_log_in_buffer_lsn;

  5. 日志刷盘和数据刷盘

    日志刷盘的规则为

    • 发出commit动作时

    • 每秒刷一次

    • 当log buffer中已经使用的内存超过一半时

    • 当有checkpoint时

    数据刷盘的规则为

    • 当有checkpoint时

    日志刷盘的次数比数据刷盘次数更多,另外即使在checkpoint时,innoDB也会**保证在写数据前,需要先写日志,这种方式称为预写日志方式(Write-Ahead Logging,WAL)。**InnoDB存储引擎通过预写日志的方式来保证事务的完整性。

    预写日志方式之所以能保证完整性,是因为日志和数据页中有LSN,通过LSN可以比较日志和数据页中数据记录的顺序。日志文件是真正的核心。

实例

来源于:详细分析MySQL事务日志(redo log和undo log)


上图中,从上到下的横线分别代表:时间轴、buffer中数据页中记录的LSN(data_in_buffer_lsn)、磁盘中数据页中记录的LSN(data_page_on_disk_lsn)、buffer中重做日志记录的LSN(redo_log_in_buffer_lsn)、磁盘中重做日志文件中记录的LSN(redo_log_on_disk_lsn)以及检查点记录的LSN(checkpoint_lsn)。

假设在最初时(12:0:00)所有的日志页和数据页都完成了刷盘,也记录好了检查点的LSN,这时它们的LSN都是完全一致的。

假设此时开启了一个事务,并立刻执行了一个update操作,执行完成后,buffer中的数据页和redo log都记录好了更新后的LSN值,假设为110。这时候如果执行 show engine innodb status 查看各LSN的值,即图中①处的位置状态,结果会是:

log sequence number(110) > log flushed up to(100) = pages flushed up to = last checkpoint at

之后又执行了一个delete语句,LSN增长到150。等到12:00:01时,触发redo log刷盘的规则(其中有一个规则是 innodb_flush_log_at_timeout 控制的默认日志刷盘频率为1秒),这时redo log file on disk中的LSN会更新到和redo log in buffer的LSN一样,所以都等于150,这时 show engine innodb status ,即图中②的位置,结果将会是:

log sequence number(150) = log flushed up to > pages flushed up to(100) = last checkpoint at

再之后,执行了一个update语句,缓存中的LSN将增长到300,即图中③的位置。

假设随后检查点出现,即图中④的位置,正如前面所说,检查点会触发数据页和日志页刷盘,但需要一定的时间来完成,所以在数据页刷盘还未完成时,检查点的LSN还是上一次检查点的LSN,但此时磁盘上数据页和日志页的LSN已经增长了,即:

log sequence number > log flushed up to 和 pages flushed up to > last checkpoint at

但是log flushed up to和pages flushed up to的大小无法确定,因为日志刷盘可能快于数据刷盘,也可能等于,还可能是慢于。但是checkpoint机制有保护数据刷盘速度是慢于日志刷盘的:当数据刷盘速度超过日志刷盘时,将会暂时停止数据刷盘,等待日志刷盘进度超过数据刷盘。

等到数据页和日志页刷盘完毕,即到了位置⑤的时候,所有的LSN都等于300。

随着时间的推移到了12:00:02,即图中位置⑥,又触发了日志刷盘的规则,但此时buffer中的日志LSN和磁盘中的日志LSN是一致的,所以不执行日志刷盘,即此时 show engine innodb status 时各种lsn都相等。

随后执行了一个insert语句,假设buffer中的LSN增长到了800,即图中位置⑦。此时各种LSN的大小和位置①时一样。

随后执行了提交动作,即位置⑧。默认情况下,提交动作会触发日志刷盘,但不会触发数据刷盘,所以 show engine innodb status 的结果是:

log sequence number = log flushed up to > pages flushed up to = last checkpoint at

最后随着时间的推移,检查点再次出现,即图中位置⑨。但是这次检查点不会触发日志刷盘,因为日志的LSN在检查点出现之前已经同步了。假设这次数据刷盘速度极快,快到一瞬间内完成而无法捕捉到状态的变化,这时 show engine innodb status 的结果将是各种LSN相等。

恢复

mysql的恢复策略是:

  1. 恢复时,先根据redo重做所有事务,包括未提交的事务
  2. 再根据undo回滚未提交的事务。

通过这种策略,保证了事务的原子性、一致性和持久性。

另外,引入了checkpoint机制,在恢复的时候,只需要从checkpoint的位置往后恢复即可

感想

  1. 网络上很多的内容可能不准确,即使是我写的这篇文章,也可能有不准确的地方,解决这个问题的最好办法是读源码
  2. 不同的技术可能实现不同,但核心原理往往都是相通的,而这些核心原理一般建立在基础知识之上
  3. 学习知识可以学习到不同的深度,看完《MySQL技术内幕:InnoDB存储引擎》便能大体掌握mysql的使用,但是了解的不深入,随着写了这两篇文章,对Mysql的了解更加深入了一些,如果需要继续深入,真的需要看源码了,这个事情感觉得耗费更多的时间。所以需要知道自己需要了解到什么水平,用有限的时间去做性价比更高的事情,选择很重要。

资料

  1. 深入学习MySQL事务:ACID特性的实现原理
  2. 详细分析MySQL事务日志(redo log和undo log)
  3. https://blog.csdn.net/suerge_storm/article/details/90484944
  4. https://blog.csdn.net/qq_41151659/article/details/99559397

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

往期文章回顾:

  1. 事务原子性、一致性、持久性的实现原理
  2. 如何锻炼自己的记忆力
  3. CDN请求过程详解
  4. 关于程序员职业发展的思考
  5. 记博客服务被压垮的历程
  6. 常用缓存技巧
  7. 如何高效对接第三方支付
  8. Gin框架简洁版
  9. 关于代码review的思考
  10. InnoDB锁与事务简析
  11. Markdown编辑器推荐-typora

事务原子性、一致性、持久性的实现原理相关推荐

  1. 数据库零碎要点001_数据库的4大特性(原子性_持久性_隔离性_一致性)_数据库的隔离级别(脏读_幻读_不可重复读)_mysql如何设置隔离级别

    本篇讲诉数据库中事务的四大特性(ACID),并且将会详细地说明事务的隔离级别. 如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: ⑴ 原子性(Atomicity) 原子性是指事务 ...

  2. 什么是事务的一致性?一致性和原子性的区别是什么?

    (PS:黄色字体为二次修改的内容) 关于事务的一致性,<数据库系统概念>中是这样描述的 第二段说的三个特性是指原子性.隔离性.持久性. 就算这样,相信大家也是懵懵的,我也是,所以才会写下这 ...

  3. MySQL怎么运行的系列(八)14张图说明白MySQL事务原子性和undo日志原理

    本系列文章目录 展开/收起 MySQL怎么运行的系列(一)mysql体系结构和存储引擎 MySQL怎么运行的系列(二)Innodb缓冲池 buffer pool 和 改良版LRU算法 Mysql怎么运 ...

  4. 详述MySQL事务及ACID特性的实现原理

    " 事务是 MySQL 等关系型数据库区别于 NoSQL 的重要方面,是保证数据一致性的重要手段. 本文将首先介绍 MySQL 事务相关的基础概念,然后介绍事务的 ACID 特性,并分析其实 ...

  5. mysql事务保证幂等_事务与一致性:刚性or柔性

    转发自 https://cloud.tencent.com/developer/article/1038871 在高并发场景下,分布式储存和处理已经是常用手段.但分布式的结构势必会带来"不一 ...

  6. 事务的四大特性及实现原理

    事务的四大特性及实现原理 1.原子性(Atomicity) 不可再分,事务的最小单位.在一个事务中,我们对数据库的操作,要么都成功,要么都失败,不可能出现部分成功或部分失败的情况.若操作失败必须回滚. ...

  7. 深入学习MySQL事务:ACID特性的实现原理

    事务是MySQL等关系型数据库区别于NoSQL的重要方面,是保证数据一致性的重要手段.本文将首先介绍MySQL事务相关的基础概念,然后介绍事务的ACID特性,并分析其实现原理. MySQL博大精深,文 ...

  8. 分布式事务最终一致性mysql_分布式事务最终一致性方案案例

    前言: 以下以网上课程购买流程举一个例子: 如何实现两个分布式服务(订单服务.学习服务)共同完成一件事即订单支付成功自动添加学生选课的需求, 这里的关键是如何保证两个分布式服务的事务的一致性. 订单支 ...

  9. 一文讲透微服务下如何保证事务的一致性

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"加群"获取公众号专属群聊入口 随着业务的快速发展.业务复杂度越来越高,传统单体应用逐渐 ...

最新文章

  1. 中国合成橡胶行业竞争趋势与运营展望规划分析报告2022年
  2. Windows上安装Mysql解压缩版教程
  3. boost::gil模块实现打包像素格式的测试程序
  4. cmd下pip安装mysql_pip安装MySQLpython
  5. 监听网页微信扫码支付成功_网付扫码点餐新福利,消费者点餐可获微信支付金币奖励...
  6. [html] 图片上传时实现本地预览功能的原理是什么?
  7. VisualSVNServerTools(在线修改VisualSVN密码)
  8. 再提手机上网应该包月计费
  9. 嵌入式实时音乐语音识别系统的实现
  10. 怎么用虚拟机安装Windows XP?
  11. protel中单位换算!mil=?mm!走线的粗细
  12. 看书和写书,简单而复杂的反思-读书分享会感悟
  13. 快速排序时间复杂度数学证明
  14. 在Ubuntu 8.10 中安装使用新一代输入法ibus Deb包下载
  15. 苹果今年或无法推出M3芯片;​微软将推私有版ChatGPT:价格是常规版10倍;sudo和su用Rust重写|极客头条
  16. 电脑系统还原节点怎么创建
  17. 鸿蒙二部曲之一,网文封神之作,“鸿蒙二部曲”和“斗罗四部曲”你选择站哪边?...
  18. 堆——神奇的优先队列 大根堆小根堆详解,附小根堆C++代码实现与STL相关
  19. adb无法连接上夜神模拟器夜神模拟器cannot connect to 127.0.0.1:62001: 由于目标计算机积极拒绝,无法连接。 (10061)
  20. round在python是什么意思_python3.3.2我对函数“round”有正确的理解吗?

热门文章

  1. Linux--JVM内存设置
  2. vue中获取并操作dom元素
  3. 【魏先生搞定Python系列】一文搞定Cufflinks画图
  4. ICPR 2020|大规模商品图像识别挑战赛冠军技术干货分享
  5. Perl中的单行注释和多行注释
  6. Python 全局变量、局部变量、静态变量 详解
  7. android usb wifi驱动下载,android 平台USB wifi驱动移植及使用
  8. 入库订单(组合关系、主从表)模型
  9. QT中引用动态库(.so) 和 静态库 (.a)
  10. MySQL索引原理理解