MySQL数据库日志文件binlog、undo以及redo

2024-05-26 19:32

本文主要是介绍MySQL数据库日志文件binlog、undo以及redo,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

WAL机制

在MySQL中,为了提高数据库的性能,MySQL采用了WAL(Write-Ahead Logging,预写日志)机制,即 客户端在修改数据的过程后,并不会立马对硬盘中的数据进行更新。用于保证数据操作的原子性和持久性

这样做的原因在于,如果每次客户端进行数据更改后,立马对磁盘中的数据进行更改的话,那么磁盘的压力是非常大的。

WAL机制: 先写日志,再写磁盘。具体说,当有一条记录需要更新的时候,InnoDB引擎就会先把记录写到redo log里面,并更新内存,这个时候更新计算完成了。同时InnoDB引擎会在在系统比较空闲的时候,将这个操作记录更新到磁盘里

可能有人会很疑惑,同样是操作磁盘,为什么写日志比直接改数据会更加高效?

答案在于 写日志是顺序写,直接改磁盘是随机写。所以同样是写,它们写的速度有着天壤之别。这也是WAL的另外一个好处。

 

MySQL 通过 redo、undo 日志实现 WAL

  • redo log 称为重做日志,每当有操作时,在数据变更之前将操作写入 redo log,这样当发生掉电之类的情况时系统可以在重启后继续操作。
  • undo log 称为撤销日志,当一些变更执行到一半无法完成时,可以根据撤销日志恢复到变更之间的状态。

MySQL 中用 redo log 来在系统 Crash 重启之类的情况时修复数据(事务的持久性),而 undo log 来保证事务的原子性。

 


redo log与undo log并非是相互的逆操作,而是能配合起来使用的两种机制。说是两种机制,其实都是日志记录,不同的是redo记录以顺序附加的形式记录新值,如某条记录<T,X,V>,表示事物T将新值V存储到数据库元素X,新值可以保证重做;而Undo记录通常以随机操作的形式记录旧值,如某条记录<T1,Y,9>,表示事物T1对Y进行了修改,修改前Y的值是9,旧值能用于撤销,也能供其他事务读取。Redo用来保证事务的持久性,Undo能保证事务的原子性,两者也是系统恢复的基础前提。

 


redo log

重做日志(redo log),每当有操作时,在数据变更之前将操作写入 redo log,这样当发生断电之类的情况时系统可以在重启后继续操作。用来保证事务的持久性,即事务ACID中的D。 redo log 是 InnoDB 引擎特有的日志

Redo log的主要作用是: 用于数据库的崩溃恢复

InnoDB 的 redo log 是固定大小的,比如可以配置为一组 4 个文件,每个文件的大小是 1GB,那么这块“粉板”总共就可以记录 4GB 的操作。从头开始写,写到末尾就又回到开头循环写,如下面这个图所示。
在这里插入图片描述

  • write pos: 是当前记录的位置,一边写一边后移,写到第 3 号文件末尾后就回到 0 号文件开头。
  • check point: 是当前要擦除的位置,也是往后推移并且循环的,擦除记录前要把记录更新到数据文件。

write pos 和 checkpoint 之间的的部分可以用来记录新的操作。如果 write pos 追上 checkpoint,表示redo log空间满了,这时候不能再执行新的更新,得停下来先擦掉一些记录,把 checkpoint 推进一下。

有了 redo log,InnoDB 就可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe

 

redo日志文件参数

  • innodb_log_buffer_size: 设置缓存空间的大小(默认16M)
  • innodb_log_file_size: 设置redo日志文件的大小(默认48M)
  • innodb_log_files_in_group: 日志文件数
  • innodb_log_group_home_dir: 日志文件存放位置

redo log总大小 = innodb_log_files_in_group * innodb_log_file_size

镜像组: MySQL5.1以上版本已经不支持redo镜像组,也就是说只能存在一组重做日志。
日志作用: 在数据库重启时可以利用redo日志进行实例恢复。
日志存储: redo日志是以块的形式存储的,每个块大小为512字节,由日志头(12)、日志内容(492)和日志尾(8)。
日志文件数: MySQLredo日志文件数量默认为2个,可以通过参数innodb_log_files_in_group进行调整。
日志文件名称: ib_logfile0、ib_logfile1,文件存放在innodb_log_group_home_dir。
日志存储内容: 记录数据库中的数据也发生的变化。
日志写入方式: 日志组中的多个日志文件之间采取循环覆盖式写入。

 

Redo的整体流程

img

  • 第一步:先将原始数据从磁盘中读入内存中来,修改数据的内存拷贝
  • 第二步:生成一条重做日志并写入redo log buffer,记录的是数据被修改后的值
  • 第三步:当事务commit时,将redo log buffer中的内容刷新到 redo log file,对 redo log file采用追加写的方式
  • 第四步:定期将内存中修改的数据刷新到磁盘中

 

innodb_flush_log_at_trx_commit参数

为了控制 redo log 的写入策略,InnoDB 提供了 innodb_flush_log_at_trx_commit 参数,它有三种可能取值:

  • innodb_flush_log_at_trx_commit = 0: Innodb 中的Log Thread 没隔1 秒钟会将log buffer中的数据写入到文件,同时还会通知文件系统进行文件同步的flush操作,保证数据确实已经写入到磁盘上面的物理文件。但是,每次事务的结束(commit 或者是rollback)并不会触发LogThread 将log buffer 中的数据写入文件。所以,当设置为0 的时候,当MySQL Crash 和OS Crash或者主机断电之后,最极端的情况是丢失1 秒时间的数据变更。
  • innodb_flush_log_at_trx_commit = 1: 这也是 Innodb的默认设置。我们每次事务的结束都会触发Log Thread 将log buffer中的数据写入文件并通知文件系统同步文件(fsync)。这个设置是最安全的设置,能够保证不论是MySQL Crash 还是OS Crash或者是主机断电都不会丢失任何已经提交的数据。
  • innodb_flush_log_at_trx_commit = 2: 当我们设置为2 的时候,Log Thread会在我们每次事务结束的时候将数据写入事务日志,但是这里的写入仅仅是调用了文件系统的文件写入(write)操作。而我们的文件系统都是有缓存机制的,所以LogThread的这个写入并不能保证内容真的已经写入到物理磁盘上面完成持久化的动作。文件系统什么时候会将缓存中的这个数据同步到物理磁盘文件LogThread 就完全不知道了。所以,当设置为2 的时候,MySQL Crash(宕机) 并不会造成数据的丢失,但是OS Crash或者是主机断电后可能丢失的数据量就完全控制在文件系统上了。各种文件系统对于自己缓存的刷新机制各不一样,大家可以自行参阅相关的手册。

 


binlog

mysql-binlog是MySQL数据库的二进制日志,用于记录用户对数据库操作的SQL语句((除了数据查询语句)信息。可以使用mysqlbin命令查看二进制日志的内容。

MySQL 通过 binlog 二进制日志恢复数据

MySQL 通过 binlog 二进制日志恢复数据

binlog 写入过程

事务执行,产生binlog cache,直到事务提交,binlog cache中的日志数据会全部写进硬盘binlog中,这个时候数据处于硬盘的page cache中。

 

binlog_format参数

mysql复制主要有三种方式:基于SQL语句的复制(statement-based replication, SBR),基于行的复制(row-based replication, RBR),混合模式复制(mixed-based replication, MBR)。对应的,binlog的格式也有三种:STATEMENT,ROW,MIXED。

  • STATEMENT模式(SBR): 每一条会修改数据的sql语句会记录到binlog中。优点是并不需要记录每一条sql语句和每一行的数据变化,减少了binlog日志量,节约IO,提高性能。缺点是在某些情况下会导致master-slave中的数据不一致(如sleep()函数, last_insert_id(),以及user-defined functions(udf)等会出现问题)

  • ROW模式(RBR): 不记录每条sql语句的上下文信息,仅需记录哪条数据被修改了,修改成什么样了。而且不会出现某些特定情况下的存储过程、或function、或trigger的调用和触发无法被正确复制的问题。缺点是会产生大量的日志,尤其是alter table的时候会让日志暴涨。

  • MIXED模式(MBR): 以上两种模式的混合使用,一般的复制使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存binlog,MySQL会根据执行的SQL语句选择日志保存方式。

mysql <=5.7.6版本默认采用statement,>=5.7.7版本采用row,建议使用mixed

 

sync_binlog参数

是否fsync由参数 sync_binlog 控制,它的值也有3中可能的状态:

  • 0: 表示每次提交事务都只 write,不 fsync。
  • 1: 表示每次提交事务都会执行 fsync。
  • N: 表示每次提交事务都 write,但累积 N 个事务后才 fsync。

sync_binlog 这个参数一般设置为1,与上面 innodb_flush_log_at_trx_commit 设置为1一起被DBA称为 "双1模式"

 

redo log 和 binlog 区别

  • redo log 是 InnoDB 引擎特有的;binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用。
    • MySQL 整体来看,其实就有两块:一块是 Server 层,它主要做的是 MySQL 功能层面的事情;还有一块是引擎层,负责存储相关的具体事宜。redo log 是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 binlog(归档日志)。
  • redo log 是物理日志,记录的是把某数据更改成了什么;binlog 是逻辑日志,记录的是更新操作的逻辑。
    • 比如 1 + 2 = 3,redo log 记录的是这个数据之前是 1,更新后是 3;而 binlog 记录的是给数据 1 加上 2。
  • redo log 是循环写的,空间固定会用完;binlog 是可以追加写入的。追加写是指 binlog 文件写到一定大小后会切换到下一个,并不会覆盖以前的日志。

可以只使用redolog而不使用binlog吗?
不可以。redolog是循环覆盖写,写到末尾要回到开头继续写,这样的日志无法保留历史记录,无法进行数据复制。

可以使用binlog替代redolog进行数据恢复吗?
不可以。innodb利用WAL技术进行数据恢复,write ahead logging技术依赖于物理日志进行数据恢复,binlog不是物理日志是逻辑日志,因此无法使用。

 

MySQL中不管是redo log还是bin log,语句从执行到写到磁盘的过程中,都是先要写到cache(redo log称之为buffer)中然后再写到磁盘,换言之就是先写内存再写磁盘。

而写到磁盘也分2种状态:

  • 1是数据保存在硬盘的paga cache中,这个过程称之为 write
  • 2是数据真正持久化到磁盘这个过程称之为 fsync

数据写cache很快,写到硬盘的page cahe也快,真正慢且消耗IO的地方再持久化的过程,也就是fsync。

 


二阶段提交(Two-phaseCommit,2PC)

MySQL使用两阶段提交 主要解决 bin log 和 redo log 的数据一致性的问题

Redo log 和bingo 有一个共同的数据字段,叫 XID,崩溃恢复的时候,会按顺序扫描 redo log。

  • 假设在写入binlog前系统崩溃,那么数据库恢复后顺序扫描 redo log,碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务,而且binlog也没写入,所以事务就直接回滚了。
  • 假设在写入binlog之后,事务提交前数据库崩溃,那么数据库恢复后顺序扫描 redo log,碰到既有 prepare、又有 commit 的 redo log,就直接提交,保证数据不丢失。

 

原理描述

  • 阶段1:InnoDB redo log 写盘,InnoDB 事务进入 prepare 状态

  • 阶段2:如果前面prepare成功,binlog 写盘,那么再继续将事务日志持久化到binlog,如果持久化成功,那么InnoDB 事务则进入 commit 状态(实际是在redo log里面写上一个commit记录)

备注: 每个事务binlog的末尾,会记录一个 XID event,标志着事务是否提交成功,也就是说,recovery 过程中,binlog 最后一个 XID event 之后的内容都应该被 purge。

为什么要采用两阶段提交: 一条SQL语句在MySQL中如何执行的#更新语句

  • 解决 redo log 和 binlog 数据一致性问题。

 


undo log

undo log主要记录的是数据的逻辑变化,为了在发生错误时回滚之前的操作,需要将之前的操作都记录下来,然后在发生错误时才可以回滚。用于保证事务的原子性

undo是一种逻辑日志,有两个作用:

  • 用于事务的回滚
  • MVCC(多版本并发控制)

 

undo日志,只将数据库逻辑地恢复到原来的样子,在回滚的时候,它实际上是做的相反的工作,比如一条INSERT ,对应一条 DELETE,对于每个UPDATE,对应一条相反的 UPDATE,将修改前的行放回去。undo日志用于事务的回滚操作进而保障了事务的原子性。

# 关于undo的参数
show variables like '%undo%';

 


References

  • MySQL中日志机制
  • 浅析MySQL事务中的redo与undo
  • MySQL 日志系统之 redo log 和 binlog
  • MySQL日志系统
  • MySQL 二阶段提交

这篇关于MySQL数据库日志文件binlog、undo以及redo的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1005379

相关文章

MyBatis 动态 SQL 优化之标签的实战与技巧(常见用法)

《MyBatis动态SQL优化之标签的实战与技巧(常见用法)》本文通过详细的示例和实际应用场景,介绍了如何有效利用这些标签来优化MyBatis配置,提升开发效率,确保SQL的高效执行和安全性,感... 目录动态SQL详解一、动态SQL的核心概念1.1 什么是动态SQL?1.2 动态SQL的优点1.3 动态S

Mysql表的简单操作(基本技能)

《Mysql表的简单操作(基本技能)》在数据库中,表的操作主要包括表的创建、查看、修改、删除等,了解如何操作这些表是数据库管理和开发的基本技能,本文给大家介绍Mysql表的简单操作,感兴趣的朋友一起看... 目录3.1 创建表 3.2 查看表结构3.3 修改表3.4 实践案例:修改表在数据库中,表的操作主要

C# WinForms存储过程操作数据库的实例讲解

《C#WinForms存储过程操作数据库的实例讲解》:本文主要介绍C#WinForms存储过程操作数据库的实例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、存储过程基础二、C# 调用流程1. 数据库连接配置2. 执行存储过程(增删改)3. 查询数据三、事务处

SpringBoot日志配置SLF4J和Logback的方法实现

《SpringBoot日志配置SLF4J和Logback的方法实现》日志记录是不可或缺的一部分,本文主要介绍了SpringBoot日志配置SLF4J和Logback的方法实现,文中通过示例代码介绍的非... 目录一、前言二、案例一:初识日志三、案例二:使用Lombok输出日志四、案例三:配置Logback一

golang 日志log与logrus示例详解

《golang日志log与logrus示例详解》log是Go语言标准库中一个简单的日志库,本文给大家介绍golang日志log与logrus示例详解,感兴趣的朋友一起看看吧... 目录一、Go 标准库 log 详解1. 功能特点2. 常用函数3. 示例代码4. 优势和局限二、第三方库 logrus 详解1.

mysql出现ERROR 2003 (HY000): Can‘t connect to MySQL server on ‘localhost‘ (10061)的解决方法

《mysql出现ERROR2003(HY000):Can‘tconnecttoMySQLserveron‘localhost‘(10061)的解决方法》本文主要介绍了mysql出现... 目录前言:第一步:第二步:第三步:总结:前言:当你想通过命令窗口想打开mysql时候发现提http://www.cpp

MySQL大表数据的分区与分库分表的实现

《MySQL大表数据的分区与分库分表的实现》数据库的分区和分库分表是两种常用的技术方案,本文主要介绍了MySQL大表数据的分区与分库分表的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录1. mysql大表数据的分区1.1 什么是分区?1.2 分区的类型1.3 分区的优点1.4 分

MySQL错误代码2058和2059的解决办法

《MySQL错误代码2058和2059的解决办法》:本文主要介绍MySQL错误代码2058和2059的解决办法,2058和2059的错误码核心都是你用的客户端工具和mysql版本的密码插件不匹配,... 目录1. 前置理解2.报错现象3.解决办法(敲重点!!!)1. php前置理解2058和2059的错误

Mysql删除几亿条数据表中的部分数据的方法实现

《Mysql删除几亿条数据表中的部分数据的方法实现》在MySQL中删除一个大表中的数据时,需要特别注意操作的性能和对系统的影响,本文主要介绍了Mysql删除几亿条数据表中的部分数据的方法实现,具有一定... 目录1、需求2、方案1. 使用 DELETE 语句分批删除2. 使用 INPLACE ALTER T

MySQL INSERT语句实现当记录不存在时插入的几种方法

《MySQLINSERT语句实现当记录不存在时插入的几种方法》MySQL的INSERT语句是用于向数据库表中插入新记录的关键命令,下面:本文主要介绍MySQLINSERT语句实现当记录不存在时... 目录使用 INSERT IGNORE使用 ON DUPLICATE KEY UPDATE使用 REPLACE