(1) 归档日志(binlog)是什么
我们经常听DBA说MySQL可以恢复到任意时刻的数据,为什么呢?
因为有binlog,准确的说有某个时间点的全量备份和这个时间点之后的实施备份
全量备份就是把截止到某个时间点的数据存一份,类似于历史账本,这个是全量备份,叫做历史归档日志。
实时备份是实时的数据,MySQL无论使用什么存储引擎,在执行新增、修改、删除语句时会写一条日志,记录更改,类似于实时记账,这条日志就是实施binlog。
(2) 归档日志(bin log)的作用
1、可以用来恢复数据库数据
2、可以用来给备库传输数据
没有行不行?
也可以,设置 set sql_log_bin=0
来关闭当前线程的binlog
或者 在my.cnf
配置文件设置skip-log-bin
永久关闭binlog
(3) 归档日志格式
binlog日志有三种格式,分别为 STATMENT、ROW 和 MIXED。
在 MySQL 5.7.7之前,默认的格式是STATEMENT,MySQL 5.7.7之后,默认值是ROW。
日志格式通过binlog-format指定。
(3.1) STATMENT
基于SQL语句的复制(statement-based replication, SBR),每一条会修改数据的sql语句会记录到binlog中。
优点:不需要记录每一行的变化,减少了binlog日志量,节约了IO, 从而提高了性能;
缺点:在某些情况下会导致主从数据不一致,比如执行sysdate()
、slepp()
等。
(3.2) ROW
基于行的复制(row-based replication, RBR),不记录每条sql语句的上下文信息,仅需记录哪条数据被修改了。
优点:不会出现某些特定情况下的存储过程、或function、或trigger的调用和触发无法被正确复制的问题;
缺点:会产生大量的日志,尤其是alter table的时候会让日志暴涨
(3.3) MIXED
基于STATMENT和ROW两种模式的混合复制(mixed-based replication, MBR),一般的复制使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存binlog
(3) 为什么会有两份日志呢
MySQL保证数据不会丢的能力主要体现在两方面:
(1)能够恢复到任何时间点的状态;
(2)能够保证MySQL在任何时间段突然奔溃,重启后之前提交的记录都不会丢失; 专业名词叫crash-safe
对于第一点将MySQL恢复到任何时间点的状态,只要保留有足够的binlog,可以通过重跑binlog来把数据恢复到任何时间点。
对于第二点的能力,也就是crash-safe,binlog无法保证,在InnoDB存储引擎中,事务提交过程中任何阶段,MySQL突然奔溃,重启后都能保证事务的完整性,已提交的数据不会丢失,未提交完整的数据会自动进行回滚。这个能力依赖的就是redo log和unod log两个日志。
因为最开始MySQL里并没有InnoDB存储引擎。
MySQL自带的引擎是MyISAM,但是MyISAM没有crash-safe的能力,binlog日志只能用于归档。
而InnoDB是另一个公司以插件形式引入MySQL的,InnoDB使用另外一套日志系统——也就是redo log
来实现crash-safe能力。
所以有2份日志
binlog用来归档,备份数据;由于binlog不支持事务,所以无法保证数据的原子性
、隔离性
和一致性
。
redo log用来crash-safe。用来保证原子性
和一致性
。
隔离性是通过undo log
来实现的。
(4) binlog设置
(4.1) sync_binlog
sync_binlog:是MySQL 的二进制日志(binary log)同步到磁盘的频率。
取值:0-N
sync_binlog=0
,当事务提交之后,MySQL不做fsync
之类的磁盘同步指令刷新binlog_cache
中的信息到磁盘,而让Filesystem自行决定什么时候来做同步,或者cache满了之后才同步到磁盘。这个是性能最好的。
sync_binlog=1
,当每进行1次事务提交之后,MySQL将进行一次fsync
之类的磁盘同步指令来将binlog_cache
中的数据强制写入磁盘。
sync_binlog=n
,当每进行n次事务提交之后,MySQL将进行一次fsync
之类的磁盘同步指令来将binlog_cache
中的数据强制写入磁盘。
(5) 思考题
(5.1) MySQL怎么知道binlog是完整的
回答:一个事务的 binlog 是有完整格式的:
statement 格式的 binlog,最后会有 COMMIT;
row 格式的 binlog,最后会有一个 XID event。
另外,在 MySQL 5.6.2 版本以后,还引入了 binlog-checksum 参数,用来验证 binlog 内容的正确性。
对于 binlog 日志由于磁盘原因,可能会在日志中间出错的情况,MySQL 可以通过校验 checksum 的结果来发现。
所以,MySQL 还是有办法验证事务 binlog 的完整性的。
(5.2) redo log 和 binlog 是怎么关联起来的
回答:它们有一个共同的数据字段,叫 XID。
崩溃恢复的时候,会按顺序扫描 redo log:
如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;
如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务。
参考资料
[1] 02 | 日志系统:一条SQL更新语句是如何执行的? - MySQL实战45讲
[2] mysql的crash-safe
[3] 详细分析MySQL事务日志(redo log和undo log)