【MySQL】之 binlog、undo log 和 redo log 详解
前言
我们知道,事务的原子性指的是:一个事务包含多个操作,这些操作要么全部执行,要么全都不执行。
那 MySQL 如何保证原子性的?答案就是:undo log。
MySQL 常见的 log 有:
- binlog
- undo log
- redo log
下面我们对这三个 log 进行讲解。
一、binlog
什么是 binlog
binlog
其实在日常的开发中是听得很多的,因为很多时候数据的更新就依赖着binlog
。
比如:我们的数据是保存在数据库里边的,现在我们对某个商品的某个字段的内容改了(数据库变更),而用户检索的出来数据是走搜索引擎的。为了让用户能搜到最新的数据,我们需要把引擎的数据也改掉。
换句话说就是:数据库的变更,搜索引擎的数据也需要变更。
于是,我们就会监听binlog
的变更,如果binlog
有变更了,那我们就需要将变更写到对应的数据源。
由上可知:binlog
记录了数据库表结构和表数据变更,比如update/delete/insert/truncate/create
,但它不会记录select
(因为这没有对表没有进行变更)。
binlog 的作用
binlog
我们可以简单理解为:存储着每条变更的SQL
语句。默认情况下是没有 binlog 日志的,只有开启才可以看到,通过修改 /etc/my.cnf 配置文件来开启 binlog 日志:
server-id=1001 # 指定 server-id
log-bin=mysqlbin # 默认配置
开启后一般放在 /var/lib/mysql 中,修改并重启数据库后会生成 mysqlbin.000001 文件。当然我们也可以修改文件存放位置,比如:
log-bin=/home/logs/mysql-bin # 指定logbin文件的日志路径
需要注意的是:
- 在 mysql 5.7 中开启 logbin 时需要指定 server-id 否则 mysql 服务将会启动失败;
- 一般binlog日志很占服务器的磁盘空间,最好定期对 binlog 做备份。
binlog 操作的相关命令:
1、查看日志开启状态:show variables like ‘log_%’
mysql> show variables like 'log_%';
+----------------------------------------+--------------------------------+
| Variable_name | Value |
+----------------------------------------+--------------------------------+
| log_bin | ON |
| log_error | /var/log/mysqld.log |
| log_output | FILE |
| log_slave_updates | OFF |
| log_syslog | OFF |
| log_warnings | 2 |
+----------------------------------------+--------------------------------+
21 rows in set (0.00 sec)
2、查看所有binlog日志列表 show master logs
mysql> show master logs;
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.000001 | 80514 |
+------------------+-----------+
1 row in set (0.00 sec)
3、查看最新一个binlog日志的编号名称,及其最后一个操作事件结束点 show master status
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 | 80514 | | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
4、刷新log日志,立刻产生一个新编号的binlog日志文件,跟重启一个效果 flush logs
5、清空所有binlog日志 reset master
6、查看 binlog 日志
mysqlbinlog --no-defaults /var/log/mysql-bin.000001
binlog 的作用
binlog 主要有两个作用:复制和恢复数据
- MySQL在公司使用的时候往往都是一主多从结构的,从服务器需要与主服务器的数据保持一致,这就是通过
binlog
来实现的 - 数据库的数据被干掉了,我们可以通过
binlog
来对数据进行恢复
因为binlog
记录了数据库表的变更,所以我们可以用binlog
进行复制(主从复制)和恢复数据。
二、redo log
假设我们执行一条 sql 语句:
update user_table set name='xxx' where id='5';
MySQL执行这条SQL语句,肯定是先把id=5
的这条记录查出来,然后将name
字段对应的值给改掉,但实际上Mysql的基本存储结构是页(记录都存在页里边),所以MySQL是先把这条记录所在的页找到,然后把该页加载到内存中,将对应记录进行修改。
现在就可能存在一个问题:如果在内存中把数据改了,还没来得及落磁盘,而此时的数据库挂了怎么办?显然这次更改就丢了。并且如果每个请求都需要将数据立马落磁盘之后,那速度会很慢,MySQL可能也顶不住。
为了解决这个问题,MySQL引入了redo log
,内存写完了,然后会写一份redo log
,这份redo log
记载着这次在某个页上做了什么修改。其实写redo log
的时候,也会有buffer
,是先写buffer
,再真正落到磁盘中的。至于从buffer
什么时候落磁盘,会有配置供我们配置。
当然,写redo log
也是需要写磁盘的,但它的好处就是顺序IO
(我们都知道顺序IO比随机IO快非常多)。
所以,redo log
的作用就是:当我们修改的时候,写完内存了,但数据还没真正写到磁盘的时候。此时我们的数据库挂了,我们可以根据redo log
来对数据进行恢复。因为redo log
是顺序IO,所以写入的速度很快,并且redo log
记载的是物理变化(xxxx页做了xxx修改),文件的体积很小,恢复速度很快。
三、binlog 和 redo log 的区别
从 binlog 和 redo log 的描述来看,它们的功能有点类似,都是用来做恢复的。但它们还是有这本质的区别的,下面我们探讨一下它们之间的区别。
存储的内容不同
-
binlog
记载的是update/delete/insert
这样的SQL语句,而redo log
记载的是物理修改的内容(xxxx页修改了xxx)。 -
redo log
记录的是数据的物理变化,binlog
记录的是数据的逻辑变化
功能不同
redo log
的作用是为持久化而生的。写完内存,如果数据库挂了,那我们可以通过redo log
来恢复内存还没来得及刷到磁盘的数据,将redo log
加载到内存里边,那内存就能恢复到挂掉之前的数据了。
binlog
的作用是复制和恢复而生的:
- 主从服务器需要保持数据的一致性,通过
binlog
来同步数据 - 如果整个数据库的数据都被删除了,
binlog
存储着所有的数据变更情况,那么可以通过binlog
来对数据进行恢复
问题来了:如果整个数据库的数据都被删除了,那我可以用redo log
的记录来恢复吗?答案是:不能
因为功能的不同,redo log
存储的是物理数据的变更,如果我们内存的数据已经刷到了磁盘了,那redo log
的数据就无效了。所以redo log
不会存储着历史所有数据的变更,文件的内容会被覆盖的。
binlog 和 redo log 的写入问题
-
redo log
是MySQL的InnoDB引擎所产生的 -
binlog
无论MySQL用什么引擎,都会有的
InnoDB是有事务的,事务的四大特性之一:持久性,就是靠redo log
来实现的(如果写入内存成功,但数据还没真正刷到磁盘,如果此时的数据库挂了,我们可以靠redo log
来恢复内存的数据,这就实现了持久性)。
上面也提到,在修改的数据的时候,binlog
会记载着变更的类容,redo log
也会记载着变更的内容。(只不过一个存储的是物理变化,一个存储的是逻辑变化)。那他们的写入顺序是什么样的呢?
redo log
事务开始的时候,就开始记录每次的变更信息,而binlog
是在事务提交的时候才记录。
那么问题来了:现在我们的前提是先写redo log
,再写binlog
,那么在写其中的某一个log
的时候,失败了会怎么办?下面我们分析一下:
- 如果写
redo log
失败了,那我们就认为这次事务有问题,回滚,不再写binlog
- 如果写
redo log
成功了,写binlog
,写binlog
写一半了,但失败了怎么办?我们还是会对这次的事务回滚,将无效的binlog
给删除(因为binlog
会影响从库的数据,所以需要做删除操作) - 如果写
redo log
和binlog
都成功了,那这次算是事务才会真正成功
简单来说:MySQL需要保证redo log
和binlog
的数据是一致的。
如果redo log
和binlog
的数据不一致,那就会出现乱*象:
- 如果
redo log
写失败了,而binlog
写成功了。那假设内存的数据还没来得及落磁盘,机器就挂掉了。那主从服务器的数据就不一致了。(从服务器通过binlog
得到最新的数据,而主服务器由于redo log
没有记载,没法恢复数据) - 如果
redo log
写成功了,而binlog
写失败了。那从服务器就拿不到最新的数据了。
所以,MySQL通过两阶段提交来保证redo log
和binlog
的数据是一致的。
下面是 binlog 和 redo log 的写入过程:
- 阶段1:InnoDB
redo log
写盘,InnoDB 事务进入prepare
状态 - 阶段2:
binlog
写盘,InooDB 事务进入commit
状态 - 每个事务
binlog
的末尾,会记录一个XID event
,标志着事务是否提交成功,也就是说,恢复过程中,binlog
最后一个 XID event 之后的内容都应该被 purge
四、undo log
undo log
主要有两个作用:回滚和多版本控制(MVCC)
在数据修改的时候,不仅记录了redo log
,还记录undo log
,如果因为某些原因导致事务失败或回滚了,可以用undo log
进行回滚。
undo log
主要存储的也是逻辑日志,比如我们要insert
一条数据了,那undo log
会记录的一条对应的delete
日志。我们要update
一条记录时,它会记录一条对应相反的update记录。
这也应该容易理解,毕竟回滚嘛,跟需要修改的操作相反就好,这样就能达到回滚的目的。因为支持回滚操作,所以我们就能保证:“一个事务包含多个操作,这些操作要么全部执行,要么全都不执行”。【原子性】
因为undo log
存储着修改之前的数据,相当于一个前版本,MVCC实现的是读写不阻塞,读的时候只要返回前一个版本的数据就行了。
上一篇: 初学Django:使用Python官方的hmac库生成JWT
下一篇: MySQL redo log
推荐阅读
-
MySQL 日志系统之 redo log 和 binlog
-
MySQL的日志(二):事务日志(redo log和undo log)
-
MySQL主库binlog(master-log)与从库relay-log关系代码详解
-
MySQL 重要参数 innodb_flush_log_at_trx_commit 和 sync_binlog
-
数据库——MySQL日志(redo log、undo log、bin log、erro log、slow query log、general log)
-
Mysql redo、undo、bin、relay log 区别
-
mysql 事务日志 redo log和undo log
-
MySQL之Undo Log和Redo Log
-
【MySQL】之 binlog、undo log 和 redo log 详解
-
MySQL原理解读——redo log、undo log和binlog