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

相关文章

详谈redis跟数据库的数据同步问题

《详谈redis跟数据库的数据同步问题》文章讨论了在Redis和数据库数据一致性问题上的解决方案,主要比较了先更新Redis缓存再更新数据库和先更新数据库再更新Redis缓存两种方案,文章指出,删除R... 目录一、Redis 数据库数据一致性的解决方案1.1、更新Redis缓存、删除Redis缓存的区别二

oracle数据库索引失效的问题及解决

《oracle数据库索引失效的问题及解决》本文总结了在Oracle数据库中索引失效的一些常见场景,包括使用isnull、isnotnull、!=、、、函数处理、like前置%查询以及范围索引和等值索引... 目录oracle数据库索引失效问题场景环境索引失效情况及验证结论一结论二结论三结论四结论五总结ora

Mysql 中的多表连接和连接类型详解

《Mysql中的多表连接和连接类型详解》这篇文章详细介绍了MySQL中的多表连接及其各种类型,包括内连接、左连接、右连接、全外连接、自连接和交叉连接,通过这些连接方式,可以将分散在不同表中的相关数据... 目录什么是多表连接?1. 内连接(INNER JOIN)2. 左连接(LEFT JOIN 或 LEFT

C#实现文件读写到SQLite数据库

《C#实现文件读写到SQLite数据库》这篇文章主要为大家详细介绍了使用C#将文件读写到SQLite数据库的几种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录1. 使用 BLOB 存储文件2. 存储文件路径3. 分块存储文件《文件读写到SQLite数据库China编程的方法》博客中,介绍了文

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

mysql重置root密码的完整步骤(适用于5.7和8.0)

《mysql重置root密码的完整步骤(适用于5.7和8.0)》:本文主要介绍mysql重置root密码的完整步骤,文中描述了如何停止MySQL服务、以管理员身份打开命令行、替换配置文件路径、修改... 目录第一步:先停止mysql服务,一定要停止!方式一:通过命令行关闭mysql服务方式二:通过服务项关闭

SQL Server数据库磁盘满了的解决办法

《SQLServer数据库磁盘满了的解决办法》系统再正常运行,我还在操作中,突然发现接口报错,后续所有接口都报错了,一查日志发现说是数据库磁盘满了,所以本文记录了SQLServer数据库磁盘满了的解... 目录问题解决方法删除数据库日志设置数据库日志大小问题今http://www.chinasem.cn天发

mysql主从及遇到的问题解决

《mysql主从及遇到的问题解决》本文详细介绍了如何使用Docker配置MySQL主从复制,首先创建了两个文件夹并分别配置了`my.cnf`文件,通过执行脚本启动容器并配置好主从关系,文中还提到了一些... 目录mysql主从及遇到问题解决遇到的问题说明总结mysql主从及遇到问题解决1.基于mysql

MySQL的索引失效的原因实例及解决方案

《MySQL的索引失效的原因实例及解决方案》这篇文章主要讨论了MySQL索引失效的常见原因及其解决方案,它涵盖了数据类型不匹配、隐式转换、函数或表达式、范围查询、LIKE查询、OR条件、全表扫描、索引... 目录1. 数据类型不匹配2. 隐式转换3. 函数或表达式4. 范围查询之后的列5. like 查询6

Linux下MySQL8.0.26安装教程

《Linux下MySQL8.0.26安装教程》文章详细介绍了如何在Linux系统上安装和配置MySQL,包括下载、解压、安装依赖、启动服务、获取默认密码、设置密码、支持远程登录以及创建表,感兴趣的朋友... 目录1.找到官网下载位置1.访问mysql存档2.下载社区版3.百度网盘中2.linux安装配置1.