• MySQL事务实现原理Redo Log


    原子性、持久性、一致性都是通过redo log 和 undo log 来完成的。redo log 用来保证事务的持久性undo log用来保证事务的原子性。Redo Log包括两部分:
    一是内存中的重做日志缓冲(Redo Log Buffer),该部分日志是易失性的
    二是磁盘上的重做日志文件(Redo Log File),该部分日志是持久的
    redo log的主要功能是用于数据库崩溃时的数据恢复。
     
    一、写日志到磁盘过程
     
    用户空间:用户进程运行的内存空间
    内核空间:操作系统进程运行的内存空间。 
     
     
    1.重做日志文件
    在默认情况,InnoDB存储引擎的数据目录下会有两个名为ib_logfile0ib_logfile1的文件。每个InnoDB存储引擎至少有1个重做日志文件(group),每个文件组下至少有2个重做日志文件。InnoDB存储引擎先写ib_logfile0,当达到文件最后时,会切换至重做日志文件ib_logfile1

    2.重做日志组

    重做日志组,是一个逻辑上的概念。InnoDB存储引擎实际只有一个log group。
    redo log file的大小对innodb的性能影响非常大
    • 设置的太大,恢复的时候就会时间较长;
    • 设置的太小,就会导致在写redo log的时候循环切换redo log file。
     
    3.mysql读取修改数据是以页为单为进行的
    MySQL将数据一页一页加载到内存中的,然后进行读取、修改操作。一为16KB,页分为数据页索引页。当进行commit时,首先是在内存中进行commit
    脏页是指内存中与磁盘上不一致的数据(并不是坏的!)
    读已提交、读未提交指的是读取了在内存中已提交/未提交的Page页。
    mysql在读取数据时,先会在内存中读取,如果page在内存中不存在,则再去磁盘上读取。当隔离级别设置为读未提交时,不管page页是否commit,会直接返回给客户端;当隔离级别设置为读已提交时,如果发现该page在内存中commit,则会再去磁盘中读取。
     
    二、Redo Log原理
    InnoDB是事务的存储引擎,其通过Force Log at Commit机制实现事务的持久性,即当事务提交(COMMIT) 时,必须先将该事务的所有日志写人到重做日志文件进行持久化,待事务的COMMIT操作完成才算完成。这里的日志是指重做日志,在InnoDB存储引擎中,由两部分组成,即redo log和undo log。redo log用来保证事务的持久性,undo log用来帮助事务回滚及MVCC的功能。redo log基本上都是顺序写的,在数据库运行时不需要对redo log的文件进行读取操作。而undo log是需要进行随机读的。
     
    一个事务要修改多张表的多条记录, 多条记录分布在不同的Page里面, 对应到磁盘的不同位置。如果每个事务都直接写磁盘,一次事务提交就要多次磁盘的随机I/O,性能达不到要求。怎么办呢?不写磁盘,在内存中进行事务提交,然后再通过后台线程异步地把内存中的数据写入磁盘中。但有个问题:机器宕机,内存中的数据还没来得及刷盘,数据丢失了(持久性)。为此, 就有了Write-Ahead Log的思路:先在内存中提交事务, 然后写日志(所谓的Write-Ahead Log) , 然后后台任务把内存中的数据异步刷到磁盘。日志是顺序地在尾部Append, 从而也就避了一个事务发生多次磁盘随机I/O的问题。明明是先在内存中提交事务,后写的日志,为什么叫作Write-Ahead呢?这里的Ahead, 其实是指相对于真正的数据刷到磁盘, 因为是先写的日志后把内存数据刷到磁盘, 所以叫Write-Ahead Log。
    通过内存提交事务解决MySQL的性能瓶颈,通过Redo Log解决持久性问题。
     
    记录Redo Log的时机
    在完成数据的修改之后,脏页刷入磁盘之前写入重做日志缓冲区。即先修改,再写入。
    在以下情况下,redo log由重做日志缓冲区写入磁盘上的重做日志文件。
    • redo log buffer的日志据redo log buffer总容量的一半时,将redo log写入磁盘。
    • 一个事务提交时,他的redo log都刷入磁盘,这样可以保证数据绝不丢失(最常见的情况)。注意这时内存中的脏页可能尚未全部写入磁盘。
    • 后台线程定时刷新,有一个后台线程每过一秒就将redo log写入磁盘。
    • MySQL关闭时,redo log都被写入磁盘。
    第一种情况和第四种情况一定会执行redo log的写入,第二种情况和第三种情况的执行要根据参数innodb_flush_log_at_trx_commit的设定值,在下文会有详细描述。
    索引的创建也需要记录redo log。
     
    内存操作数据 + Write-Ahead Log的这种思想非常普遍, 后面讲LSM树的时候, 还会再次提到这个思想。在多备份一致性中,复制状态机的模型也是基于此。具体到InnoDB中, Wrte-Ahead Log是Redo Log。在InnoDB中, 不光事务修改的数据库表数据是异步刷盘的, 连Redo Log的写入本身也是异步的。如图6-7所示, 在事务提交之后,Redo Log先写入到内存中的Redo Log Buffer中, 然后异步地刷到磁盘上的Redo Log。
    为此, InnoDB有个关键的参数innodb_flush_log_at_tx_commit控制Redo Log的刷盘策略,该参数有三个取值:
    0:每秒刷一次磁盘, 把Redo Log Buffer中的数据刷到Redo Log(默认为0) 。
    1:每提交一个事务,就刷一次磁盘(这个最安全)。
    2:不刷盘。然后根据参数innodb_flush_log_at_timeout 设置的值决定刷盘频率。
    很显然,该多数设置为0或者2都可镜丢失数据,设为1最安全,但性能最差。InnoDB设置此参数,也是为了让应用在数据安全性和性能之间做一个权衡。
     
    刷盘规则
    • 发出commit动作时。已经说明过,commit发出后是否刷日志由变量 innodb_flush_log_at_trx_commit 控制。
    • 每秒刷一次。这个刷日志的频率由变量 innodb_flush_log_at_timeout 值决定,默认是1秒。要注意,这个刷日志频率和commit动作无关。
    • 当log buffer中已经使用的内存超过一半时。
    • 当有checkpoint时,checkpoint在一定程度上代表了刷到磁盘时日志所处的LSN位置。
     
     
    三、Rolo Log结构
    知道了Rolo Log的本设计思想, 下面来看Redo Log的详细结构。
    从逻辑上来讲,日志就是一个无限适长的字节流,从数据库安装好并启动的时间点开始,日志便源不断地追加,永无结束。
    但从物理上来讲,日志不可能是一个永不束的字节流,日志的物理结构和逻辑结构,两个非常显著的差异点:
    (1)磁盘的读取和写入都不是按一个个平节来处理的,磁盘是“块”设备,为了保证磁盘的I/O效率, 都是整块地读取和写入, 对于Redo Log来说,就是Rodo Log Block, 每个Redo Log Block是512字节。为什么是512字节呢?因为早期的磁盘, 一个扇区(最细粒度的磁盘存储单位)就是存储512字节数据。
    (2)日志文件不可能无限制膨张,过了一定时期。之前的的历史日志不需要了。通俗地讲叫“归档, 专业术语是CheckPoint。所以Rolo Log其实是一个固定大小的文件, 循环使用,写到尾部之后, 回到头部覆写(实际Redo Log是一组文件, 但这里就这当成一个大文件,不影响对原理的理解) 。之所以覆写, 因为一旦Page数据刷到磁盘上。日志数据就没有存在的必要了。
     
    Redo Log格式
    默认情况下,innodb的页大小是16KB(由innodb_page_size 变量控制),一个页内可以存放非常多的log block(每个512字节),而log block中记录的又是数据页的变化。在InnoDB存储引擎中,重做日志都是以512字节进行存储的。意味着重做日志缓存、重做日志文件都是以块(block)的方式进行保存的,每块512字节。
    日志Header 12字节,日志Tailer 8字节,日志Body 492字节
    日志块包含4部分:
    • log_block_hdr_no:(4字节)该日志块在redo log buffer中的位置ID。
    • log_block_hdr_data_len:(2字节)该log block中已记录的log大小。写满该log block时为0x200,表示512字节。
    • log_block_first_rec_group:(2字节)该log block中第一个log的开始偏移位置。
    • lock_block_checkpoint_no:(4字节)写入检查点信息的位置。
    其中log body的格式分为4部分:
    • redo_log_type:占用1个字节,表示redo log的日志类型。
    • space:表示表空间的ID,采用压缩的方式后,占用的空间可能小于4字节。
    • page_no:表示页的偏移量,同样是压缩过的。
    • redo_log_body表示每个重做日志的数据部分,恢复时会调用相应的函数进行解析。例如insert语句和delete语句写入redo log的内容是不一样的。
    日志只有一个部分: log_block_trl_no ,该值和块头的 log_block_hdr_no 相等
    如下图,分别是insert和delete大致的记录方式
     
     
     
  • 相关阅读:
    Jquery练手 DEMO 全选 取消 反选 复选框 的实现
    ValidateTemplate(Label lb2, boolean iflag) {
    Android中更新UI的三种方式
    Doodle Android图片涂鸦,具有撤消、缩放、移动、添加文字,贴图等功能。还是一个功能强大,可自定义和可扩展的涂鸦框架、多功能画板。
    怎么用 javascript 实现拖拽
    System.Attribute 类的使用
    javascript 操作Cookie
    导航全局滑动JavaScript
    Sql server DATEADD() 函数
    类序列化
  • 原文地址:https://www.cnblogs.com/caoxb/p/15526164.html
Copyright © 2020-2023  润新知