MySQL 日志 主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。其中,比较重要的还要属二进制日志 binlog(归档日志)和事务日志 redo log(重做日志)和 undo log(回滚日志)
一、redo log
1、用法
innodb独有的日志,一旦mysql主节点发生重启,innodb就会根据redo log来恢复数据,保证数据的持久性与完整性。
从节点或备节点是通过同步主节点的binlog来完成数据恢复的。
MySQL 中数据是以页为单位,在有缓存机制的mysql版本中,查询是会从磁盘中读取一个数据页放入buffer pool中,命中就直接返回
在写入数据时,先更新buffer pool,再把“在某个数据页上做了什么修改”写入redo log,然后刷盘到redolog文件中,这与redis的AOF日志简直一模一样
2、刷盘时机
(1)事务提交时
(2)log buffer 空间不足时
(3)事务日志缓冲区满:InnoDB 使用一个事务日志缓冲区(transaction log buffer)来暂时存储事务的重做日志条目。当缓冲区满时,会触发日志的刷新,将日志写入磁盘。
(4)checkpoint(检查点):InnoDB 定期会执行检查点操作,将内存中的脏数据(已修改但尚未写入磁盘的数据)刷新到磁盘,并且会将相应的重做日志一同刷新,以确保数据的一致性。
(5)后台刷新线程:InnoDB 启动了一个后台线程,负责周期性(每隔 1 秒)地将脏页(已修改但尚未写入磁盘的数据页)刷新到磁盘,并将相关的重做日志一同刷新。
(6)正常关闭服务器:MySQL 关闭的时候,redo log 都会刷入到磁盘里去。
可以通过参数innodb_flush_log_at_trx_commit来设置刷盘时机
(1)innodb_flush_log_at_trx_commit=0:表示每次事务提交时不进行刷盘操作。这种方式性能最高,但是也最不安全,因为如果 MySQL 挂了或宕机了,可能会丢失最近 1 秒内的事务(因为后台线程每1秒刷新一次)。
(2)innodb_flush_log_at_trx_commit=1(默认):表示每次事务提交时都将进行刷盘操作。这种方式性能最低,但是也最安全,因为只要事务提交成功,redo log 记录就一定在磁盘里,不会有任何数据丢失。
(3)innodb_flush_log_at_trx_commit=2:表示每次事务提交时都只把 log buffer 里的 redo log 内容写入 page cache(文件系统缓存)。page cache 是专门用来缓存文件的,这里被缓存的文件就是 redo log 文件。这种方式的性能和安全性都介于前两者中间。
3、日志文件组
redo log 日志文件不只一个,而是以一个日志文件组的形式出现的,每个的redo日志文件大小都是一样的。
假设有4个日志文件,每个文件1G,
它采用的是环形数组形式,从头开始写,写到末尾又回到头循环写
两个重要参数:
write pos 是当前记录的位置,一边写一边后移
checkpoint 是当前要擦除的位置,也是往后推移
每次刷盘 redo log 记录到日志文件组中,write pos 位置就会后移更新。
每次 MySQL 加载日志文件组恢复数据时,会清空加载过的 redo log 记录,并把 checkpoint 后移更新。
write pos 追上 checkpoint时,表示日志文件组满了,这时候不能再写入新的 redo log 记录,MySQL 会清空一些记录,把 checkpoint 推进一下
MySQL 8.0.30 之前可以通过 innodb_log_files_in_group 和 innodb_log_file_size 配置日志文件组的文件数和文件大小,之后就是固定的32个文件,这两个参数相乘得到文件组总容量innodb_redo_log_capacity,每个文件容量就是innodb_redo_log_capacity/32
二、binlog
1、作用
redo log 它是物理日志,记录内容是“在某个数据页上做了什么修改”,属于 InnoDB 存储引擎。
而 binlog 是逻辑日志,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”,属于MySQL Server 层。
MySQL 数据库的数据备份、主备、主主、主从都离不开 binlog,需要依靠 binlog 来同步数据,保证数据一致性
2、记录格式
binlog_format参数指定三种记录格式:statement、row、mixed
指定statement,记录的内容是SQL语句原文,比如执行一条update T set update_time=now() where id=1,记录的内容如下
同步数据时,会执行记录的SQL语句,但是有个问题,update_time=now()这里会获取当前系统时间,直接执行会导致与原库的数据不一致
所以需要指定为row,记录的内容不再是简单的SQL语句了,还包含操作的具体数据,记录内容如下
row格式记录的内容看不到详细信息,要通过mysqlbinlog工具解析出来
但是这种格式,需要更大的容量来记录,比较占用空间,恢复与同步时会更消耗 IO 资源,影响执行速度。所以就有了一种折中的方案,指定为mixed,记录的内容是前两者的混合。MySQL 会判断这条SQL语句是否可能引起数据不一致,如果是,就用row格式,否则就用statement格式。
3、写入时机
事务执行过程中,先把日志写到binlog cache,事务提交的时候,再把binlog cache写到 binlog 文件中
刷盘时机也和redo log差不多,只是最后2可以改为N,表示N个事务执行完后,一把刷盘
4、两阶段提交事务
在执行更新语句过程,会记录 redo log 与 binlog 两块日志,以基本的事务为单位,redo log 在事务执行过程中可以不断写入,而 binlog 只有在提交事务时才写入,所以 redo log 与 binlog 的写入时机不一样。
写入binlog时发生异常,redo log的数据和binlog的数据就会不一致
为了解决两份日志之间的逻辑一致问题,InnoDB 存储引擎使用两阶段提交方案。
原理很简单,将 redo log 的写入拆成了两个步骤prepare和commit,这就是两阶段提交。
使用两阶段提交后,写入 binlog 时发生异常也不会有影响,因为 MySQL 根据 redo log 日志恢复数据时,redo log有数据,binlog没数据,但redo log还处于prepare阶段,就会回滚该事务。
即便redolog在提交阶段出现问题,MySQL 根据 redo log 日志恢复数据时,redo log有数据,binlog也有数据,那不管redo log时prepare还是commit,都会恢复该数据
三、undo log
每一个事务对数据的修改都会被记录到 undo log ,当执行事务过程中出现错误或者需要执行回滚操作的话,MySQL 可以利用 undo log 将数据恢复到事务开始之前的状态。