6.5 事务实现原理之1:Redo Log

介绍事务怎么用后,下面探讨事务的实现原理。事务有ACID四个核心属性:
A:原子性。事务要么不执行,要么完全执行。如果执行到一半,宕机重启,已执行的一半要回滚回去。
C:一致性。各种约束条件,比如主键不能为空、参照完整性等。
I:隔离性。隔离性和并发性密切相关,因为如果事务全是串行的(第四个隔离级别),也不需要隔离。
D:持久性。这个很容易理解,一旦事务提交了,数据就不能丢。
在这四个属性中,D比较容易,C主要是由上层的各种规则来约束,也相对简单。而A和I牵涉并发问题、崩溃恢复的问题,将是讨论的重点。

说到事务的实现原理,会追溯到ARIES算法理论,ARIES(Algorithms for Recovery AndIsolation Expoliting Semantics)是20世纪90年代由IBM的几位研究员提出的一个算法集,主论文是ARIES: A TransactionRecovery Method Supporting Fine-Granularity Locking and Partial Rollbacks UsingWrite-Ahead Loggging。ARIES的思想影响深远,现代的关系型数据库(DB2、MySQL、InnoDB、SQL Server、Oracle)在事务实现的很多方面都吸收了该思想,在大学的教科书上如果讲到事务的实现,也都会介绍AREIS方法。
接下来,就以InnoDB为背景,分析事务的ACID其中的三个属性(A、I、D)是如何实现的。先从最简单的D开始(I/O问题),然后是A,最后讨论I。

6.5.1 Write-Ahead
一个事务要修改多张表的多条记录,多条记录分布在不同的Page里面,对应到磁盘的不同位置。如果每个事务都直接写磁盘,一次事务提交就要多次磁盘的随机I/O,性能达不到要求。怎么办呢?不写磁盘,在内存中进行事务提交。然后再通过后台线程,异步地把内存中的数据写入到磁盘中。但有个问题:机器宕机,内存中的数据还没来得及刷盘,数据就丢失了。
为此,就有了Write-aheadLog的思路:先在内存中提交事务,然后写日志(所谓的Write-ahead Log),然后后台任务把内存中的数据异步刷到磁盘。日志是顺序地在尾部Append,从而也就避免了一个事务发生多次磁盘随机I/O的问题。明明是先在内存中提交事务,后写的日志,为什么叫作Write-Ahead呢?这里的Ahead,其实是指相对于真正的数据刷到磁盘,因为是先写的日志,后把内存数据刷到磁盘,所以叫Write-Ahead Log。
内存操作数据 +Write-Ahead Log的这种思想非常普遍,后面讲LSM树的时候,还会再次提到这个思想。在多备份一致性中,复制状态机的模型也是基于此。
具体到InnoDB中,Write-Ahead Log是Redo Log。在InnoDB中,不光事务修改的数据库表数据是异步刷盘的,连Redo Log的写入本身也是异步的。如图6-7所示,在事务提交之后,Redo Log先写入到内存中的Redo Log Buffer中,然后异步地刷到磁盘上的Redo Log。
为此,InnoDB有个关键的参数innodb_flush_log_at_trx_commit控制Redo Log的刷盘策略,该参数有三个取值:
0:每秒刷一次磁盘,把Redo Log Buffer中的数据刷到Redo Log(默认为0)。
1:每提交一个事务,就刷一次磁盘(这个最安全)。
2:不刷盘。然后根据参数innodb_flush_log_at_timeout设置的值决定刷盘频率。
很显然,该参数设置为0或者2都可能丢失数据。设置为1最安全,但性能最差。InnoDB设置此参数,也是为了让应用在数据安全性和性能之间做一个权衡。

图6-7 Redo Log的异步刷盘示意图

6.5.2 Redo Log的逻辑与物理结构
知道了Redo Log的基本设计思想,下面来看Redo Log的详细结构。
从逻辑上来讲,日志就是一个无限延长的字节流,从数据库安装好并启动的时间点开始,日志便源源不断地追加,永无结束。
但从物理上来讲,日志不可能是一个永不结束的字节流,日志的物理结构和逻辑结构,有两个非常显著的差异点:
(1)磁盘的读取和写入都不是按一个个字节来处理的,磁盘是“块”设备,为了保证磁盘的I/O效率,都是整块地读取和写入。对于Redo Log来说,就是Redo Log Block,每个Redo Log Block是512字节。为什么是512字节呢?因为早期的磁盘,一个扇区(最细粒度的磁盘存储单位)就是存储512字节数据。
(2)日志文件不可能无限制膨胀,过了一定时期,之前的历史日志就不需要了,通俗地讲叫“归档”,专业术语是Checkpoint。所以Redo Log其实是一个固定大小的文件,循环使用,写到尾部之后,回到头部覆写(实际Redo Log是一组文件,但这里就当成一个大文件,不影响对原理的理解)。之所以能覆写,因为一旦Page数据刷到磁盘上,日志数据就没有存在的必要了。
图6-8展示了Redo Log逻辑与物理结构的差异,LSN(Log Sequence Number)是逻辑上日志按照时间顺序从小到大的编号。在InnoDB中,LSN是一个64位的整数,取的是从数据库安装启动开始,到当前所写入的总的日志字节数。实际上LSN没有从0开始,而是从8192开始,这个是InnoDB源代码里面的一个常量LOG_START_LSN。因为事务有大有小,每个事务产生的日志数据量是不一样的,所以日志是变长记录,因此LSN是单调递增的,但肯定不是呈单调连续递增。

图6-8 Redo Log逻辑结构与物理结构的差异

物理上面,一个固定的文件大小,每512个字节一个Block,循环使用。显然,很容易通过LSN换算出所属的Block。反过来,给定Redo Log,也很容易算出第一条日志在什么位置。假设在Redo Log中,从头到尾所记录的LSN依次如下所示:
(200,289,378,478,30,46,58,69,129)
很显然,第1条日志是30,最后1条日志是478,30以前的已经被覆盖。

6.5.3 Physiological Logging
知道了Redo Log的整体结构,下面进一步来看每个Log Block里面Log的存储格式。这个问题很关键,是数据库事务实现的一个核心点。
(1)记法1。类似Binlog的statement格式,记原始的SQL语句,insert/delete/update。
(2) 记法2。类似Binlog的RAW格式,记录每张表的每条记录的修改前的值、修改后的值,类似(表,行,修改前的值,修改后的值)。
(3) 记法3。记录修改的每个Page的字节数据。由于每个Page有16KB,记录这16KB里哪些部分被修改了。一个Page如果被修改了多个地方,就会有多条物理日志,如下所示:
(Page ID,offset1,len1,改之前的值,改之后的值)
(Page ID,offset2,len2,改之前的值,改之后的值)
前两种记法都是逻辑记法;第三种是物理记法。Redo Log采用了哪种记法呢?它采用了逻辑和物理的综合体,就是先以Page为单位记录日志,每个Page里面再采取逻辑记法(记录Page里面的哪一行被修改了)。这种记法有个专业术语,叫PhysiologicalLogging。
要搞清楚为什么要采用PhysiologicalLogging,就得知道逻辑日志和物理日志的对应关系:
(1)一条逻辑日志可能产生多个Page的物理日志。比如往某个表中插入一条记录,逻辑上是一条日志,但物理上可能会操作两个以上的Page?为什么呢,因为一个表可能有多个索引,每个索引都是一颗B+树,插入一条记录,同时更新多个索引,自然可能修改多个Page。
如果Redo Log采用逻辑日志的记法,一条记录牵涉的多个Page写到一半系统宕机了,要恢复的时候很难知道到底哪个Page写成功了,哪个失败了。
(2)即使1条逻辑日志只对应一个Page,也可能要修改这个Page的多个地方。因为一个Page里面的记录是用链表串联的,所以如果在中间插入一条记录,不仅要插入数据,还要修改记录前后的链表指针。对应到Page就是多个位置要修改,会产生多条物理日志。
所以纯粹的逻辑日志宕机后不好恢复;物理日志又太大,一条逻辑日志就可能对应多条物理日志。Physiological Logging综合了两种记法的优点,先以Page为单位记录日志,在每个Page里面再采用逻辑记法。

6.5.4 I/O写入的原子性(Double Write)
要实现事务的原子性,先得考虑磁盘I/O的原子性。一个LogBlock是512个字节。假设调用操作系统的一次Write,往磁盘上写入一个Log Block(512个字节),如果写到一半机器宕机后再重启,请问写入成功的字节数是0,还是[0,512]之间的任意一个数值?
这个问题的答案并不唯一,可能与操作系统底层和磁盘的机制有关,如果底层实现了512个字节写入的原子性,上层就不需要做什么事情;否则,在上层就需要考虑这个问题。假设底层没有保证512个字节的原子性,可以通过在日志中加入checksum解决。通过checksum能判断出宕机之后重启,一个Log Block是否完整。如果不完整,就可以丢弃这个LogBlock,对日志来说,就是做截断操作。
除了日志写入有原子性问题,数据写入的原子性问题更大。一个Page有16KB,往磁盘上刷盘,如果刷到一半系统宕机再重启,请问这个Page是什么状态?在这种情况下,Page既不是一个脏的Page,也不是一个干净的Page,而是一个损坏的Page。既然已经有Redo Log了,不能用Redo Log恢复这个Page吗?
因为Redo Log也恢复不了。因为Redo Log是Physiological Logging,里面只是一个对Page的修改的逻辑记录,Redo Log记录了哪个地方修改了,但不知道哪个地方损坏了。另外,即使为这个Page加了checksum,也只能判断出Page损坏了,只能丢弃,但无法恢复数据。有两个解决办法:
(1)让硬件支持16KB写入的原子性。要么写入0个字节,要么16KB全部成功。
(2)Doublewrite。把16KB写入到一个临时的磁盘位置,写入成功后再拷贝到目标磁盘位置。
这样,即使目标磁盘位置的16KB因为宕机被损坏了,还可以用备份去恢复。

Redo Log的原理比较复杂,在接下来的1篇中,将接着这个话题继续探讨。

后记: 本文节选自作者书籍《软件架构设计:大型网站技术架构与业务架构融合之道》。
作者微信公众号:架构之道与术。公众号底部菜单有书友群可以加入,与作者和其他读者进行深入讨论。也可以在京东、天猫上购买纸质书籍。

数据库原理 - 序列3 - 事务是如何实现的? - Redo Log解析的更多相关文章

  1. 数据库原理 - 序列5 - 事务是如何实现的? - Undo Log解析

    本文节选自作者书籍<软件架构设计:大型网站技术架构与业务架构融合之道>.作者微信公众号:架构之道与术.公众号底部菜单有书友群可以加入,与作者和其他读者进行深入讨论.也可以在京东.天猫上购买 ...

  2. 数据库原理 - 序列4 - 事务是如何实现的? - Redo Log解析(续)

    > 本文节选自<软件架构设计:大型网站技术架构与业务架构融合之道>第6.4章节. 作者微信公众号:> 架构之道与术.进入后,可以加入书友群,与作者和其他读者进行深入讨论.也可以 ...

  3. 数据库原理 - 序列7 - Binlog与主从复制

    本文节选自作者书籍<软件架构设计:大型网站技术架构与业务架构融合之道>.作者微信公众号:架构之道与术.公众号底部菜单有书友群可以加入,与作者和其他读者进行深入讨论.也可以在京东.天猫上购买 ...

  4. 数据库中的两个最重要的日志redo log和binlog

    mysql整体来看其实只有两部分,一部分是server层,一部分是引擎层. 1.redo log(重做日志):当有一条记录需要更新的时候,InnoDB 引擎就会先把记录写入redo log里面,并更新 ...

  5. [转]undo log与redo log原理分析

    数据库通常借助日志来实现事务,常见的有undo log.redo log,undo/redo log都能保证事务特性,这里主要是原子性和持久性,即事务相关的操作,要么全做,要么不做,并且修改的数据能得 ...

  6. InnoDB事务日志(redo log 和 undo log)详解

    数据库通常借助日志来实现事务,常见的有undo log.redo log,undo/redo log都能保证事务特性,undolog实现事务原子性,redolog实现事务的持久性. 为了最大程度避免数 ...

  7. 详细分析MySQL事务日志(redo log和undo log)

    innodb事务日志包括redo log和undo log.redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作. undo log不是redo log的逆向过程,其实它 ...

  8. 【MySQL (六) | 详细分析MySQL事务日志redo log】

    Reference:  https://www.cnblogs.com/f-ck-need-u/archive/2018/05/08/9010872.html 引言 为了最大程度避免数据写入时 IO ...

  9. 详细分析MySQL事务日志(redo log和undo log) 表明了为何mysql不会丢数据

    innodb事务日志包括redo log和undo log.redo log是重做日志,提供前滚操作,undo log是回滚日志,提供回滚操作. undo log不是redo log的逆向过程,其实它 ...

随机推荐

  1. 豆瓣API

    Api V2 索引 图书Api V2 电影Api V2 音乐Api V2 同城Api V2 广播Api V2 用户Api V2 日记Api V2 相册Api V2 线上活动Api V2 论坛Api V ...

  2. OAuth 2.0 认证的原理与实践

    摘要: 使用 OAuth 2.0 认证的的好处是显然易见的.你只需要用同一个账号密码,就能在各个网站进行访问,而免去了在每个网站都进行注册的繁琐过程. 本文将介绍 OAuth 2.0 的原理,并基于 ...

  3. 19.最省钱的app发短信方法

    在创业团队中,一个重要的原则是能省就省,该花就花,把银子用在刀刃上. 现在的app,为了获取用户的社交关系,需要用户的手机号注册.用手机号注册就涉及到一个发送短信验证码的问题,那怎么才能在短信服务上投 ...

  4. Python 员工信息管理系统

    学Python将近一个月了,第一次写了两百多行代码,一个很简单的脚本. 员工信息管理系统: 需求: 1.管理员账户能够增加,删除,修改,查询员工信息,并且设置管理员账户. 2.普通账户可以查看所有员工 ...

  5. 学习笔记1--响应式网页+Bootstrap起步+全局CSS样式

    一.学习之前要了解一些背景知识: 在2g时代,3g时代,4g时代,早期的网页浏览设备,功能机,智能机.(本人最喜欢的透明肌,和古典黑莓机) 1.什么是响应式网页? Responsive Web Pag ...

  6. bzoj5249 [2018多省省队联测]IIIDX

    转化一下问题变成给定一棵树,一个序列,求父亲的权值小于子树的最大方案. 直接贪心会在有重复权值时出现错误,我们考虑用线段树优化贪心. 将序列从小到大排序,线段树上每个点记录他和他右边当前还可用的权值, ...

  7. BZOJ_4892_[Tjoi2017]dna_哈希

    BZOJ_4892_[Tjoi2017]dna_哈希 Description 加里敦大学的生物研究所,发现了决定人喜不喜欢吃藕的基因序列S,有这个序列的碱基序列就会表现出喜欢吃藕的 性状,但是研究人员 ...

  8. (5)STM32使用HAL库实现串口通讯——实战操作

    功能需求: (1)对接收的字符串原样返回(每10个字符一次). (2)发送一个字符串完成后改变LED的状态. 1.创建工程 使用的是F407Discovery,4个LED对应PD12-PD14. (1 ...

  9. Google 的 QUIC 华丽转身成为下一代网络协议: HTTP/3.0

    HTTP/2.0 还没有普及,HTTP/3.0 标准就要被制定了. 据 IETF 透露,HTTP-over-QUIC 实验协议将被重命名为 HTTP/3,并成为 HTTP 协议的第三个正式版本. IE ...

  10. Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFa

    Springboot通过application启动报错 2019-01-25 14:02:33.810 ERROR [restartedMain] org.springframework.boot.S ...