MySQL 日志 主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。其中,比较重要的还要属二进制日志 binlog(归档日志)和事务日志 redo log(重做日志)和 undo log(回滚日志)。
我们先看一张图,大概看一下这些日志的工作流程:
在存储引擎与最后紫色区域的磁盘之间应该还有系统缓存OS Buffer(有的地方也叫page cache)
Redo Log
Redo Log也叫重做日志,是InnoDB存储引擎下才有的,记录的是物理日志,也就是磁盘数据页的修改,通过它可以实现事务持久性。
比如 MySQL 实例挂了或宕机了,重启时,InnoDB存储引擎会使用redo log恢复数据,保证数据的持久性与完整性。
为了提高性能,MySQL不会把修改内容直接写入到redo log文件里面,而是先写入到redo log buffer日志缓冲中去。如图的第四步与第五步,从图中可以看出,redo log是存储引擎的。
redo log刷盘时机
具体什么时候可以把Redo Log Buffer刷到Redo Log FIle中,可以通过innodb_flush_log_at_trx_commit参数配置决定。
0(延迟写) 提交事务后,不会立即刷到OS Buffer中,而是等一秒后刷新到OS Buffer并调用fsync()写入Redo Log FIle,可能会丢失一秒钟的数据。
1(实时写)【默认值】 每次提交事务,都会刷新到OS Buffer并调用fsync()写到Redo Log FIle,性能较差
2(延迟刷新) 每次提交事务只刷新到OS Buffer,一秒后再调用fsync()写入Redo Log FIle。
Undo Log
undo log也叫回滚日志,也是InnoDB存储引擎下才有的,记录的是逻辑日志,也就是SQL语句,比如:当我们执行一条insert语句时,Undo Log就记录一条相反的delete语句。通过它可以实现**:**
- 事务回滚:可以对提前写入的数据变动进行擦除,实现事务回滚,保证事务的原子性。
- 实现 MVCC 机制:Undo Log 也用于实现 MVCC 机制(实现依赖于:隐藏字段、Read View、undo log。在内部实现中,
InnoDB通过数据行的DB_TRX_ID和Read View来判断数据的可见性,如不可见,则通过数据行的DB_ROLL_PTR找到undo log中的历史版本。每个事务读到的数据版本可能是不一样的,在同一个事务中,用户只能看到该事务创建Read View之前已经提交的修改和该事务本身做的修改),存储记录的多个版本的 Undo Log,形成版本链。
binlog
binlog 是逻辑日志,记录内容是语句的原始逻辑,类似于“给 ID=2 这一行的 c 字段加 1”,属于MySQL Server 层。
不管用什么存储引擎,只要发生了表数据更新,都会产生 binlog 日志。
开启Binlog日志有以下两个最重要的使用场景:
-
主从复制:在主库中开启Binlog功能,这样主库就可以把Binlog传递给从库,从库拿到Binlog后实现数据恢复达到主从数据一致性。
-
数据恢复:假如不小心删库删表了,可以通过mysqlbinlog工具来恢复数据。
写入机制
binlog的写入时机也非常简单,事务执行过程中,先把日志写到binlog cache,事务提交的时候,再把binlog cache写到binlog文件中。
因为一个事务的binlog不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个块内存作为binlog cache。
我们可以通过binlog_cache_size参数控制单个线程 binlog cache 大小,如果存储内容超过了这个参数,就要暂存到磁盘(Swap)。
binlog日志刷盘流程如下
两阶段提交
redo log(重做日志)让InnoDB存储引擎拥有了崩溃恢复能力。
binlog(归档日志)保证了MySQL集群架构的数据一致性。
虽然它们都属于持久化的保证,但是侧重点不同。
在执行更新语句过程,会记录redo log与binlog两块日志,以基本的事务为单位,redo log在事务执行过程中可以不断写入,而binlog只有在提交事务时才写入,所以redo log与binlog的写入时机不一样。
假如一个update语句,他会记录redo log和binlog。假设执行过程中写完redo log日志后,binlog日志写期间发生了异常,会出现什么情况呢?
如果在主从复制场景下,主库是最新的值,但是从库依然是旧值,因为主从复制是通过binlog进行同步的,现在binlog异常了,自然出现这个问题。为了解决两份日志之间的逻辑一致问题,InnoDB存储引擎使用两阶段提交方案。
原理很简单,将redo log的写入拆成了两个步骤prepare和commit,这就是两阶段提交
使用两阶段提交后,写入binlog时发生异常也不会有影响,因为MySQL根据redo log日志恢复数据时,发现redo log还处于prepare阶段,并且没有对应binlog日志,就会回滚该事务。
再看一个场景,redo log设置commit阶段发生异常,那会不会回滚事务呢?
并不会回滚事务,它会执行上图框住的逻辑,虽然redo log是处于prepare阶段,但是能通过事务id找到对应的binlog日志,所以MySQL认为是完整的,就会提交事务恢复数据。
Redo Log和Binlog区别
-
Redo Log是属于InnoDB引擎功能,Binlog是属于MySQL Server自带功能,并且是以二进制文件记录。
-
Redo Log属于物理日志,记录该数据页更新状态内容,Binlog是逻辑日志,记录更新过程。
-
Redo Log日志是循环写,日志空间大小是固定,Binlog是追加写入,写完一个写下一个,不会覆盖使用
Buffer pool
MySQL 中数据是以页为单位,你查询一条记录,会从硬盘把一页的数据加载出来,加载出来的数据叫数据页,会放入到 Buffer Pool 中。
后续的查询都是先从 Buffer Pool 中找,没有命中再去硬盘加载,减少硬盘 IO 开销,提升性能。
更新表数据的时候,也是如此,发现 Buffer Pool 里存在要更新的数据,就直接在 Buffer Pool 里更新。
看文章开头的那张图,buffer pool通过redo log和undo log两个文件来更新内容,然后会有一个I/O线程以page页为单位随机写入数据库的磁盘(第八步)。
参考文章: