WAL(Write Ahead Logging):先写日志、再写磁盘。这也是Mysql在更新数据时的机制,Mysql提供了bin log、redo log、undo log三个核心日志。
问题1:Mysql为什么要先写日志再写磁盘?
如果直接写磁盘为随机写,开销大性能低,无法满足数据库的性能要求。
问题2:Mysql为什么不先写内存再写磁盘?
直接写内存无法避免异常宕机带来的数据丢失,无法保证数据的持久性。
01|bin log
binlog是记录所有数据库表结构变更以及表数据修改的二进制日志,不会记录SELECT和SHOW这类操作。binlog日志以事件的形式进行记录,还包含语句所执行的消耗时间,开启binlog日志有以下两个重要的使用场景:
- 主从复制:在主库开启binlog功能,将binlog传递到从库,从库拿到binlog后实现数据恢复达到主从数据一致性。
ROW(row-based replication):日志中会记录每一行数据被修改的情况,然后在slave端对相同的数据进行修改。
- 优点:能清楚记录每一个行数据的修改细节,能完全实现主从数据同步和数据的恢复
- 缺点:批量操作会产生大量日志,尤其是alter table会让日志暴涨
- STATMENT(statement-based replication):记录所有修改数据的sql,slave在复制的时候SQL进行会解析成和原来master端执行过的相同的SQL再次执行,简称SQL语句复制。
- 优点:日志量小,减少磁盘IO,存储和恢复速度快
- 缺点:某些情况下会导致主从数据不一致,如last_inser_id(),now()等函数
MIXED(mixed-based replication):以上两种模式的混合使用,一般会使用STATEMENT模式保存binlog,对于STATEMENT模式无法复制的操作使用ROW模式保存,会根据执行SQL语句选择写入模式
Binlog文件结构
binlog文件中记录的是对数据库的各种修改操作,用来表示修改操作的数据结构是log event,binlog文件的内容就是各种log event的集合。
binlog写入机制:写入时会根据记录模式和操作类型生成log event,生成的log event会先保存在一个binlog_cache_mngr数据结构的缓冲区上,其中又分为trx_cache和stmt_cache分别存储事务和非事务的信息。事务相关的log event只有在提交阶段才会将信息写入到外部的binlog文件中。不同事务以串行的方式将log event写入binlog文件中,所以一个事务包含的log event信息在binlog文件中是连续的,中间不会插入其他事务的log event。事务执行过程中,先把日志写到binlog cache,事务提交的时候,再把binlog cache写到bin log文件中。一个事务的binlog是不能被拆开的,因此不论这个事务多大,也要确保一次性写入。事务提交的时候,执行器会把binlog cache里的完整事务写入到bin log中,并清空bin log cache。如图每个线程事务都有自己的binlog cache 但是公用一份binlog文件
- 图中write过程只是将日志写入文件系统的page cache,此时并未把数据持久化到磁盘
- 图中的fsync才是真正数据落盘的操作
- write和fsync的时机由sysc_binlog控制:
- sync_binlog=0:每次提交事务只write,不fsync
- sync_binlog=1:每次提交事务都会执行fsync(默认值)
- sync_binlog=N(N>1):每次提交事务都write,但是累计N个事务之后才会fsync(如果主机发生异常宕机,会丢失最近N个事务的binlog日志)
Binlog操作
binlog状态查看
开启binlog功能 ```shellshow variables like 'log_bin';
修改my.cnf配置文件
[mysqld] default-storage-engine=INNODB character-set-server=utf8 log-bin = mysql-bin binlog-format = ROW server_id = 1 port = 3306 [client] default-character-set=utf8
重启mysql
02|redo log
redo log是InnoDB引擎特有的,是为了实现事务的持久性而出现的产物,防止在发生故障的时间点,尚有脏页未写入表的IBD文件中,在重启Mysql服务的时候,根据redo log进行重做,从而达到事务的未入磁盘但数据依旧能够保证数据的持久化。redo log文件内容是以顺序循环的方式写入文件,写满时则回溯到第一个文件,进行覆盖写。
redo log二阶段提交提供了crash-safe能力。
redo log写入机制
事务执行的过程中,生成的redo log需要先写到redo log buffer然后再写入磁盘。执行过程中redo log可能存在的三种状态如图:
三种状态分别是:
- 红色:写入redo log buffer中,物理上是在mysql进程的内存中
- 黄色:写入磁盘(write),但是还未持久化(fsync),物理上是在文件系统的page cache中
- 绿色:持久化到磁盘
为了控制redo log的写入策略,InnoDB提供了innodb_flush_log_at_trx_commit参数,他有如下三种取值:
- 0:每次提交事务只是把redo log写入redo log buffer中
- 1:每次提交事务都将redo log直接持久化到磁盘
- 2:每次提交事务只将redo log写到page cache
InnoDB会启动一个后台线程,每个1秒,就不会redo log buffer中的日志,write到page cache中,然后调用fsync持久化到磁盘
redo log和binlog区别
- redo log属于InnoDB引擎功能,binlog是Mysql自带功能
- redo log属于物理日志,记录该数据页更新状态内容,binlog是逻辑日志,记录更新过程
- redo log日志是循环写,日志空间大小是固定,binlog是追加写入
- redo log作为服务器异常宕机后事务数据自动恢复使用,提供crash-safe能力;binlog可以作为主从复制和数据恢复使用,但不具备crash-safe能力。
03|undo log
undo log提供回滚以及MVCC(多行版本控制)的功能,保证事务的原子性,在数据修改的流程中,会记录一条与当前操作相反的逻辑日志到 undo log中,如果因为某些原因导致事务异常失败,可以使用undo log进行回滚,从而保证事务的完整性。
**04|崩溃恢复
当mysql崩溃重启后会检查redo log中是完整并且处于prepare状态的事务,然后根据XID(事务ID)从binlog中查找对应的事务,如找不到则回滚,找到并且事务完整则重新commit redo log完成事务的提交。
总结
WAL机制主要得益于两个方面:
- redo log和binlog都是顺序写,磁盘的顺序写比随机写速度要快
- 组提交机制,可以大幅度降低磁盘的IOPS消耗