「这是我参与11月更文挑战的第6天,活动详情查看:​​2021最后一次更文挑战​​」

MySQL进阶系列:你需要了解的几种MySQL日志_后端

MySQL有多种日志来保证实现其各种功能,我们最常见或者常听说的是Binlog(二进制日志)Redo Log(重做日志)Undo Log(回滚日志) 。其实还有Relay Log(中继日志) ,General Query Log(一般查询日志),Slow Query Log(慢查询日志),Error Log(错误日志),DDL Log(DDL 日志)等。

一. 二进制日志(binlog)

binlog是记录MySQL中执行变更(增删改)操作的日志(Binary Log由包含描述数据库内容修改的“事件”的文件组成,以二进制格式追加写入这些文件) 。binlog是MySQL的日志 在server层,所以无论是什么存储引擎都会有此日志。

binlog有什么作用

复制: 目前我们常说的高可用/高并发,那数据库的高可用/高并发就可以通过各个数据库实例之间复制binlog以达到数据的同步。如​​mysql的主从复制​

恢复: 某些数据可以通过执行binlog来恢复。

其实binlog的用处很大的,除了上面官方提到的两种作用外,还可以拓展其使用范围,如模拟mysql的协议,通过监控binlog的变化实现自己的特殊需求。对于复杂点的项目会使用redis和MQ,就会涉及到缓存一致性以及MQ消息处理,这时候就可以通过监控binlog的变化实现对redis/本地缓存/MQ的操作(像Alibaba开源项目Canal),减少代码的侵入。

MySQL中binlog的三种格式


statementrow(默认)和mixed。在 MySQL 5.7.7之前,默认的格式是STATEMENT,MySQL 5.7.7之后,默认值是ROW


mysql> show global VARIABLES like '%binlog_format%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW |
+---------------+-------+
1 row in set (0.06 sec)
  • statement(基于语句的日志记录):每一条会修改数据的sql都会记录在binlog中。不需要记录每一行的变化,减少了binlog日志量,节约了IO,提高性能。由于sql的执行是有上下文的,因此在保存的时候需要保存相关的信息,同时还有一些使用了函数之类的语句无法被记录复制(参数配置:--binlog-format=STATEMENT)。
  • row(基于行的日志记录):不记录sql语句上下文相关信息,仅保存哪条记录被修改。记录单元为每一行的改动,基本是可以全部记下来但是由于很多操作,会导致大量行的改动(比如alter table),因此这种模式的文件保存的信息太多,日志量太大(参数配置:--binlog-format=ROW)。
  • mixed(混合日志记录):上面两种折中的方案,普通操作使用statement格式记录,当无法使用statement的时候使用row格式,例如sql中有函数UUID()等(参数配置:--binlog-format=MIXED)。
binlog的写入机制


binlog的写入逻辑比较简单:事务执行过程中,先把日志写到binlog cache,当server收到事务的commit命令后,在commit执行之前把binlog cache写到binlog文件中。而非事务表的更新在执行后立即存储在binlog文件中。

一个事务的binlog是不能被拆开的,因此不论这个事务多大,也要确保一次性写入。这就涉及到了binlog cache的保存问题。

系统给binlog cache分配了一片内存,每个线程一个,参数 binlog_cache_size用于控制单个线程内binlog cache所占内存的大小。如果超过了这个参数规定的大小,就要暂存到临时文件中,当线程结束时,临时文件会被删除。

事务提交的时候,执行器把binlog cache里的完整事务写入到binlog中,并清空binlog cache。

MySQL进阶系列:你需要了解的几种MySQL日志_数据_02

如上图write只是把日志写入文件系统的page cache(操作系统内存的一部分),但是真正实现持久化还是要进行刷盘即执行fsync,执行fsync的时机是由参数sync_binlog控制的

  1. sync_binlog=0的时候,表示每次提交事务都只write ,不fsync(不安全,但是块);
  2. sync_binlog=1的时候,表示每次提交事务都write 和 fsync(最安全,但最慢);
  3. sync_binlog=N(N>1)的时候,表示每次提交事务都write ,但累积N个事务后才fsync(安全和速度折中)。

因此,在出现IO瓶颈的场景里,将sync_binlog设置成一个比较大的值,可以提升性能。在实际的业务场景中,考虑到丢失日志量的可控性,一般不建议将这个参数设成0,比较常见的是将其设置为100~1000中的某个数值。

但是将sync_binlog设置为N,对应的风险是:如果主机发生异常重启,会丢失最近N个事务的 binlog日志。

来源:丁奇《MySQL实战45讲》


二,重做日志(Redo Log)

Redo Log是InnoDB中保证数据持久性的,可以在崩溃修复期间纠正不完整事务写入的数据。

Redo Log的使用可以减少磁盘的操作,通过顺序写代替随机写

那究竟Redo Log中记录的是什么:记录的是某数据的具体位置的变更情况,比如说某个事务将系统表空间中的第100号页面中偏移量为1000处的那个字节的值 1 改成 2 我们只需要记录为:将第0号表空间的100号页面的偏移量为1000处的值更新为 2 。

当这句话已经记录到Redo Log的磁盘后,即使系统奔溃了也可以在重启之后根据这句话去修复对应数据,这个就是重做

Redo Log的写入机制


先写入Redo Log buffer,在写入page cache(操作系统内存的一部分),最后持久化磁盘中。

  1. 首先把日志写入到mysql进程中缓存中,即Redo Log buffer.(缓存操作,很快)
  2. 接着是把日志写入到物理上的文件系统page cache里(速度很快)
  3. 最后就是持久化到真正的磁盘上(这个相对较慢)

InnoDB提供了innodb_flush_log_at_trx_commit参数控制Redo Log的写入方式

  1. 设置为0的时候,表示每次事务提交时都只是把Redo Log留在Redo Log buffer中;
  2. 设置为1的时候,表示每次事务提交时都将Redo Log直接持久化到磁盘;
  3. 设置为2的时候,表示每次事务提交时都只是把Redo Log写到page cache。

InnoDB有一个后台线程,每隔1秒,就会把Redo Log buffer中的日志,调用write写到文件系统的 page cache,然后调用fsync持久化到磁盘。

来源:丁奇《MySQL实战45讲》


三,回滚日志(Undo Log)

Undo Log 其实就是保证数据的原子性,就是在事务失败回滚的时候可以把数据恢复到原样。为了在事务失败之后能够恢复,就需要在Undo Log可以记录原来的数据是什么样子。

当进行insert操作的时候,产生的Undo Log只有在事务回滚的时候需求,如果不回滚在事务提交之后就会被删除。

当进行update和delete的时候,产生的Undo Log不仅仅在事务回滚的时候需要,在快照读的时候也是需要的,所以不会立即删除,只有等不在用到这个日志的时候才会被mysql purge线程统一处理掉(delete操作也只是打一个删除标记,并不是真正的删除)。

Undo Log中还会涉及一个概念叫版本链,所谓的版本链就是多个事务操作同一条记录的时候都会生成一个Undo Log,这些Undo Log通过回滚指针串联在一起。

本文主要说的是binlog,Redo Log,Undo Log三个主要的日志文件,那关于他们三个是怎么配合使用的,可以参考以前的文章​​多版本并发控制-mvcc​​​ ,​​一条sql是怎么执行的​​​,​​MySQL主从复制和原理​​。

四,总结

binlog: server层的日志,通过复制实现数据同步,如主从复制,还有就是数据恢复。

Redo Log: 保证数据的持久性,可以在崩溃修复期间纠正不完整事务写入的数据。

Undo Log: 保证数据的原子性,实现事务失败回滚,是实现mvcc功能的一个主要条件。

Slow Query Log: 可以用来分析系统中慢查询,可以针对性优化。

Relay Log:主从复制用来存放master上获取的binlog,可以参考主从复制一文。


参考:MySQL实战45讲,MySQL官网,MySQL是怎样运行的




我是纪先生,用输出倒逼输入而持续学习,持续分享技术系列文章,以及全网值得收藏好文,欢迎关注公众号,做一个持续成长的技术人。