在之前的文章里,我们深入探讨了InnoDB引擎特有的Redo Log(重做日志),明白了它是如何保证数据断电不丢失的。很多新手朋友在学完Redo Log后,紧接着就会遇到另一个名字很像的日志:Binlog(二进制日志)。
这时候大家往往会一头雾水:既然有了Redo Log,为什么还要搞一个Binlog?它们到底有什么区别?今天,我就结合自己最近的学习笔记,用大白话把Binlog这个MySQL的"官方档案"彻底盘明白。
一、 什么是Binlog?它和Redo Log有啥区别?
首先我们要明确一个概念:Redo Log是InnoDB存储引擎特有的"底层物理日记",而Binlog是MySQL Server层实现的"官方逻辑档案"。这意味着,无论你底层用的是InnoDB还是以前老版本的MyISAM,只要你用的是MySQL,就都有Binlog。
1. 记录的内容:只记"动作",不记"查询"
Binlog专门用来记录所有对数据库产生实质性改变的操作。比如表结构的变更(DDL,像CREATE TABLE、ALTER TABLE)和表数据的修改(DML,像INSERT、UPDATE、DELETE)。
这里有个新手容易踩坑的地方:Binlog绝对不会记录SELECT、SHOW等查询类操作。因为查询不改变数据,没必要浪费磁盘空间去记录。
2. 写入机制:好记性不如烂笔头,且绝不"撕日记"
Binlog采用的是追加写的方式。当一个日志文件写满后,MySQL会自动创建一个新文件继续写,永远不会覆盖历史日志。这就像写日记,只往后写新页,绝不撕掉前面的旧页。而且,为了保证数据的完整性,一个事务在执行期间产生的所有Binlog,会在事务最终提交的那一刻,统一写入到文件中。
3. 核心作用:备份与主从复制的基石
Binlog保存了数据库自创建以来的全量变更日志,它主要有两大核心用途:
- 数据备份与恢复:如果你的数据库不小心被删库了,只要你有昨天的全量备份,再加上Binlog里记录的今天所有的"修改动作",就能通过重放这些动作,把数据完美恢复到删库前的那一秒。
- 主从复制:这是现代高并发架构的标配。主库把修改数据的动作写进Binlog,从库的IO线程把主库的Binlog拉取过来,然后由SQL线程重新执行一遍这些动作。这样,从库的数据就和主库保持同步了。
二、 Binlog的三种记录格式:该怎么选?
既然Binlog记录的是修改动作,那它具体是怎么记的呢?MySQL提供了三种记录格式,可以通过binlog_format参数进行配置。我们可以用"抄作业"来生动理解这三种格式。
1. STATEMENT(语句格式):抄写解题步骤
- 原理:它记录的是每一条修改数据的原始SQL语句,属于逻辑日志。从库拿到这些SQL后,自己重新执行一遍。
- 优点:日志量非常小。比如你执行了一条UPDATE语句,把一百万条数据的某个字段都加了1,Binlog里只会记录这一条SQL语句,非常省空间。
- 缺点:存在致命的数据不一致风险。假设你的SQL里用到了动态函数,比如UPDATE time SET create_time = NOW()。主库执行时记录的是NOW(),从库拿到这条SQL重新执行时,时间已经过去了好几秒,两边记录的时间就不一样了。再比如使用了UUID()函数,主从生成的UUID也会完全不同。
2. ROW(行格式):直接抄写最终答案
- 原理:它不记录你执行了什么SQL,而是直接记录行数据被修改后的最终状态,属于物理日志。从库拿到后,直接把对应行的数据覆盖掉。从MySQL 5.7.7版本起,ROW成为了默认格式,目前主流的5.7、8.0甚至最新的8.4版本,都在默认使用它。
- 优点:精确记录数据变化,彻底解决了STATEMENT模式下因为动态函数或复杂逻辑导致的主从数据不一致问题。主库改成什么样,从库就是什么样,绝对忠诚。
- 缺点:日志量极大。还是刚才那个更新一百万条数据的例子,在ROW格式下,Binlog不会只记一条SQL,而是会把这一百万行数据修改前和修改后的具体值,一条一条全部记录下来。这会导致Binlog文件体积瞬间暴涨,占用大量磁盘空间和网络带宽。
3. MIXED(混合格式):智能切换的课代表
- 原理:它结合了STATEMENT和ROW的特点。MySQL内部有一套判断逻辑,对于普通的SQL语句,它使用STATEMENT格式以节省空间;一旦发现SQL中包含了NOW()、UUID()等可能导致不一致的动态函数或特殊操作,它会自动切换成ROW格式,记录具体的行变化。
- 现状:虽然看起来很智能,但在实际的高并发和复杂业务场景下,MIXED格式依然可能出现一些难以排查的边界问题。因此,现在的主流实践中,大家更倾向于直接使用ROW格式。
三、 给新手的实战建议
了解了底层原理,我们在实际项目中该如何配置和使用Binlog呢?
1. 无脑选择ROW格式
在绝大多数互联网业务场景中,数据的一致性是不可逾越的红线。虽然ROW格式会让日志变大,但现在磁盘空间相对廉价,且可以通过配置Binlog过期时间(如expire_logs_days或binlog_expire_logs_seconds参数)来自动清理旧日志。为了主从数据的绝对一致,请坚持使用ROW格式。
2. 关注Binlog对磁盘的消耗
因为ROW格式日志量大,如果业务中有频繁的大批量UPDATE或DELETE操作,Binlog可能会在极短时间内把磁盘撑爆。作为新手,在写代码时一定要避免"一个大事务更新全表"的操作,尽量分批处理,给磁盘和主从同步留出喘息的时间。
3. 学会查看Binlog
MySQL提供了一个自带的命令行工具mysqlbinlog。当你需要排查主从同步延迟,或者需要找回误删的数据时,学会使用这个工具去解析Binlog文件,将是你进阶为高级开发工程师的必备技能。
四、 总结
作为新手,理清Redo Log和Binlog的关系,是我们构建完整数据库知识体系的关键一步。
Redo Log是InnoDB引擎为了应对崩溃恢复而准备的"物理草稿",而Binlog是MySQL Server层为了备份和主从复制而建立的"官方档案"。Binlog通过STATEMENT、ROW、MIXED三种格式,在日志大小和数据一致性之间做着权衡。在现代数据库架构中,ROW格式凭借其绝对的一致性,成为了当之无愧的主流选择。