背景
日志是mysql 数据库的重要组成部分,记录着数据库运行期间各种状态信息。mysql日志主要包括错误日志、查询日志、慢查询日志、事务日志、二进制日志几大类。作为开发,我们重点需要关注的是二进制日志(bin log)和事务日志(包括redo log和undo log)。
注意:在数据库系统中,既有存放数据的文件,也有存放日志的文件。
redo log
redo log基本概念
当数据库对数据做修改的时候,需要把数据页从磁盘读到buffer pool中,然后在buffer pool中进行修改,那么这个时候buffer pool中的数据页就与磁盘上的数据页内容不一致,称buffer pool的数据页为dirty page 脏数据,如果这个时候发生非正常的DB服务重启,那么这些数据还没在内存,并没有同步到磁盘文件中(注意,同步到磁盘文件是个随机IO),也就是会发生数据丢失,如果这个时候,能够在有一个文件,当buffer pool 中的data page变更结束后,把相应修改记录记录到这个文件(注意,记录日志是顺序IO),那么当DB服务发生crash的情况,恢复DB的时候,也可以根据这个文件的记录内容,重新应用到磁盘文件,数据保持一致。
redo log是InnoDB存储引擎层的日志,又被称为重写日志,用来记录事务操作的变化,记录的是数据修改之后的值,不管事务提交是否成功,都会被记录下来。
这种先写日志,后写磁盘的技术就是mysql里面经常提及到的WAL(Write Ahead Logging)技术。
具体的来说,就是当有一条记录需要更新的时候,InnoDB引擎会把记录优先更新到redo log里面,并更新内存,这样更新操作就完成了。同时,InnoDB引擎会在空闲的时间将redo log中的记录存储到磁盘上(异步写入磁盘)。
redo log 记录方式
由于redo log记录的是数据页的变更,而这种记录是没必要永久保存的,因此redo log实现上采用大小固定,循环写入的方式,当记录写到末尾时,又会从头开始写,如下图所示:
如图所示,write pos是当前记录的位置,一边写一边后移,写到4号文件末尾就回到1号文件开头。check point是当前要把记录写入到数据文件的位置,也是后移并且循环的。
写redo log也是需要写磁盘的,但它的好处就是顺序IO(我们都知道顺序IO比随机IO快非常多)。写入的速度很快。
redo log使用场景
用于系统奔溃恢复(crash-safe)。
crash-safe概念
InnoDB 可以保证即使数据库发生异常重启,之前提交的记录都不会丢失,这个能力称为 crash-safe。
举个列子:当我们修改的时候,写完内存了(buffer),但数据还没真正写到磁盘的时候。此时我们的数据库挂了,我们可以对数据进行恢复。
bin log
bin log基本概念
bin log是mysql数据库service层的,是所有存储引擎共享的日志模块,它用于记录数据库执行的写入性操作(存储着每条变更的SQL语句),也就是在事务commit阶段进行记录,以二进制的形式保存于磁盘中。
bin log是逻辑日志,并且由mysql数据库的service层执行,也就是说使用所有的存储引擎数据库都会记录bin log日志。
bin log是以追加的方式进行写入的,可以通过max_binlog_size参数设置bin log文件大小,当文件大小达到某个值时,会生成新的文件来保存日志。
bin log刷盘机制
对于InnoDB引擎而言,在每次事务`commit`提交时才会记录bin log日志,此时记录仍然在内存中,那么什么时候存储到磁盘中呢?mysql通过 `sync_binlog` 参数控制bin log刷盘时机,取值范围:0~N:
- 0:不去强求,由系统自行判断何时写入磁盘;
- 1:每次事务`commit`的时候都要将bin log写入磁盘;
- N:每N个事务`commit`,才会将bin log写入磁盘;
sync_binlog参数建议设置为1,这样每次事务commit时就会把bin log写入磁盘中,这样也可以保证mysql异常重启之后bin log日志不会丢失。
bin log使用场景
在实际场景中, bin log
的主要场景有两点,一点是主从复制,另一点是数据恢复。
- 主从复制:在master端开启
bin log
,然后将bin log
发送给各个slaver端,slaver端读取bin log
日志,从而使得主从数据库中数据一致 - 数据恢复:通过
bin log
获取想要恢复的时间段数据
undo log
undo log基本概念
undo log
是回滚日志,是记录每条数据的所有版本,比如 update
语句,那么它首先会将该条记录的数据记录到undo log
日志中,并且将最新版本的roll_pointer指针指向上一个版本,这样就可以形成当前记录的所有版本,这也是MVCC的实现机制。
bin log和redo log的区别
1. redo log 是 InnoDB 引擎特有的,binlog 是 MySQL 的 Server 层实现的,所有引擎都可以使用
2. redo log 是物理日志,记录的是在某个数据页上做了什么修改,binlog 是逻辑日志,记录的是这个语句的原始逻辑,比如给 ID=2 这一行的 c 字段加 1
3. 基于redo log直接恢复数据的效率高于基于binlog sql语句恢复
4. binlog不是循环使用,在写满或者重启之后,会生成新的binlog文件,redo log是循环使用
5. binlog可以作为恢复数据使用,主从复制搭建,redo log作为异常宕机或者介质故障后的数据恢复使用
两阶段提交
MySQL通过两阶段提交来保证redo log和binlog的数据是一致的。当我执行一条 update 语句时,redo log 和 binlog 是在什么时候被写入的呢?
写入:redo log(该条记录处于prepare状态)
写入:binlog
写入:redo log(该条记录处于commit状态)
为什么 redo log 要分两个阶段: prepare 和 commit ?redo log 就不能一次写入吗?我们可以先分别看下一次写入会出现的问题。
1. 先写 redo log,再写 binlog
这样会出现 redo log 写入到磁盘了,但是 binlog 还没写入磁盘,于是当发生 crash recovery 时,恢复后,主库会应用 redo log,恢复数据,但是由于没有 binlog,从库就不会同步这些数据,主库比从库“新”,造成主从不一致。
2. 先写 binlog,再写 redo log
跟上一种情况类似,很容易知道,这样会反过来,造成从库比主库“新”,也会造成主从不一致。
而两阶段提交,就可以解决掉上述问题。
当出现crash recovery时,恢复后,如果 redo log 已经 commit,那毫不犹豫的,把事务提交;如果 redo log 处于 prepare,则去判断事务对应的 binlog 是不是完整的;是,则把事务提交;否,则事务回滚。
bin log/redo log/undo log区别
参考文档:
https://www.jianshu.com/p/125fbcb11206
https://zhuanlan.zhihu.com/p/341376691