mysql · 2022-04-04 0

MySQL中的三种日志

三种日志文件

  1. binlog 二进制日志
  2. redo log 重做日志
  3. undo log 回滚日志

一、binlog

binlog是用于记录数据库表结构和表数据变更的二进制日志,比如insert、update、delete、create、truncate等等操作,不会记录select、show操作,因为没有对数据本身发生变更。

1.组成

binlog 包括两类文件:

二进制日志索引文件(.index):记录所有的二进制文件。
二进制日志文件(.00000*):记录所有 DDL 和 DML 语句事件。

flush logs 刷新一下日志文件,重新开始一个 Binlog 日志。我们插入一条数据之后看一下日志。

mysql> flush logs;
Query OK, 0 rows affected (0.04 sec)

mysql> show master logs;
+---------------+-----------+-----------+
| Log_name      | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 |   3107351 | No        |
| binlog.000002 |      1153 | No        |
| binlog.000003 |       156 | No        |
+---------------+-----------+-----------+
3 rows in set (0.00 sec)

mysql> insert into test.student values(3, 3);
Query OK, 1 row affected (0.00 sec)

mysql> show master logs;
+---------------+-----------+-----------+
| Log_name      | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 |   3107351 | No        |
| binlog.000002 |      1153 | No        |
| binlog.000003 |       435 | No        |
+---------------+-----------+-----------+
3 rows in set (0.00 sec)
mysql> show binlog events in 'binlog.000003';
+---------------+-----+----------------+-----------+-------------+--------------------------------------+
| Log_name      | Pos | Event_type     | Server_id | End_log_pos | Info                                 |
+---------------+-----+----------------+-----------+-------------+--------------------------------------+
| binlog.000003 |   4 | Format_desc    |         1 |         125 | Server ver: 8.0.22, Binlog ver: 4    |
| binlog.000003 | 125 | Previous_gtids |         1 |         156 |                                      |
| binlog.000003 | 156 | Anonymous_Gtid |         1 |         235 | SET @@SESSION.GTID_NEXT= 'ANONYMOUS' |
| binlog.000003 | 235 | Query          |         1 |         306 | BEGIN                                |
| binlog.000003 | 306 | Table_map      |         1 |         360 | table_id: 91 (test.student)          |
| binlog.000003 | 360 | Write_rows     |         1 |         404 | table_id: 91 flags: STMT_END_F       |
| binlog.000003 | 404 | Xid            |         1 |         435 | COMMIT /* xid=58 */                  |
+---------------+-----+----------------+-----------+-------------+--------------------------------------+
7 rows in set (0.00 sec)

2.binlog 写入磁盘

sync_binlog 这个参数设置成 1 的时候,表示每次事务的 binlog 都持久化到磁盘

mysql> show variables like 'sync_binlog';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| sync_binlog   | 1     |
+---------------+-------+
1 row in set (0.01 sec)

mysql> show master logs;
+---------------+-----------+-----------+
| Log_name      | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 |   3107351 | No        |
| binlog.000002 |      1153 | No        |
| binlog.000003 |       435 | No        |
+---------------+-----------+-----------+
3 rows in set (0.00 sec)

mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into test.student values(4, 4);
Query OK, 1 row affected (0.01 sec)

mysql> show master logs;
+---------------+-----------+-----------+
| Log_name      | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 |   3107351 | No        |
| binlog.000002 |      1153 | No        |
| binlog.000003 |       435 | No        |
+---------------+-----------+-----------+
3 rows in set (0.00 sec)

mysql> commit;
Query OK, 0 rows affected (0.04 sec)

mysql> show master logs;
+---------------+-----------+-----------+
| Log_name      | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000001 |   3107351 | No        |
| binlog.000002 |      1153 | No        |
| binlog.000003 |       714 | No        |
+---------------+-----------+-----------+
3 rows in set (0.00 sec)

3.sync_binlog 参数

mysql> show variables like 'sync_binlog';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| sync_binlog   | 1     |
+---------------+-------+
  • sync_binlog=0,表示MySQL不控制binlog的刷新,由文件系统自己控制它的缓存的刷新。这时候的性能是最好的,但是风险也是最大的。因为一旦系统Crash,在binlog_cache中的所有binlog信息都会被丢失
  • 如果sync_binlog>0,表示每sync_binlog次事务提交,MySQL调用文件系统的刷新操作将缓存刷下去。最安全的就是sync_binlog=1了,表示每次事务提交,MySQL都会把binlog刷下去,是最安全但是性能损耗最大的设置。这样的话,在数据库所在的主机操作系统损坏或者突然掉电的情况下,系统才有可能丢失1个事务的数据。但是binlog虽然是顺序IO,但是设置sync_binlog=1,多个事务同时提交,同样很大的影响MySQL和IO性能

很多MySQL DBA设置的sync_binlog并不是最安全的1,而是100或者是0。这样牺牲一定的一致性,可以获得更高的并发和性能。

二、redo log

redo log重做日志文件,用于记录事务操作的变化,记录的是数据修改之后的值,不管事务是否提交都会记录下来。在实例和介质失败(media failure)时,redo log文件就能派上用场,如数据库掉电,InnoDB存储引擎会使用redo log恢复到掉电前的时刻,以此来保证数据的完整性。

redo log重做日志,这个日志是由MySql的 innodb存储引擎 提供维护的,此日志文件只存在于innodb存储引擎下;

1.组成

redo log 包括两部分:一个是内存中的日志缓冲( redo log buffer ),另一个是磁盘上的日志文件( redo logfile)。

每个InnoDB存储引擎至少有1个重做日志文件组(group),每个文件组至少有2个重做日志文件,如默认的ib_logfile0和ib_logfile1。

2.redo log 写入磁盘

mysql 每执行一条 DML 语句,先将记录写入 redo log buffer,它就是一块内存,后续某个时间点再一次性将多个操作记录(根据 innodb_flush_log_at_trx_commit 参数)写到 redo log file。

这种 先写日志,再写磁盘 的技术就是 MySQL里经常说到的 WAL(Write-Ahead Logging) 技术。

redo log 是循环写的,redo log 不是记录数据页更新之后的状态,而是记录这个页做了什么改动。

3.innodb_flush_log_at_trx_commit 参数

mysql> show variables like 'innodb_flush_log_at_trx_commit';
+--------------------------------+-------+
| Variable_name                  | Value |
+--------------------------------+-------+
| innodb_flush_log_at_trx_commit | 1     |
+--------------------------------+-------+
  • innodb_flush_log_at_trx_commit=0,在提交事务时,InnoDB不会立即触发将缓存日志写到磁盘文件的操作,而是每秒触发一次缓存日志回写磁盘操作,并调用操作系统fsync刷新IO缓存。
  • innodb_flush_log_at_trx_commit=1,在每个事务提交时,InnoDB立即将缓存中的redo日志回写到日志文件,并调用操作系统fsync刷新IO缓存。
  • innodb_flush_log_at_trx_commit=2,在每个事务提交时,InnoDB立即将缓存中的redo日志回写到日志文件,但并不马上调用fsync来刷新IO缓存,而是每秒只做一次磁盘IO缓存刷新操作。

三、 undo log

undo log的作用主要用于回滚,mysql数据库的事务的原子性就是通过undo log实现的。

undo log记录的是数据修改之前的数据。

undo log另一个作用是实现多版本控制(MVCC),undo记录中包含了记录更改前的镜像,如果更改数据的事务未提交,对于隔离级别大于等于read commit的事务而言,不应该返回更改后数据,而应该返回老版本的数据。

四、update 语句时的内部流程

  1. 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
  2. 执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
  3. 引擎将这行新数据更新到内存(InnoDB Buffer Pool)中,同时将这个更新操作记录到 redo log 里面,此时 redo log buffer 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
  4. 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
  5. 执行器调用引擎的提交事务接口,引擎把刚刚写入的 redo log buffer 改成提交(commit)状态,更新完成。

每次事务提交时,不用同步刷新磁盘数据文件,只需要同步刷新redo log就足够了。相比写数据文件时的随机IO,写Redo log时的顺序IO能够提高事务提交速度。

五、脏页

1.脏页

真实的执行过程是,当我们想更新或者读取某条数据的时候,会把对应的数据页(默认是16k)加载到内存中的Buffer Pool缓冲池中(默认为128m)

当更新数据的时候,如果对应的页在Buffer Pool中,则直接更新Buffer Pool中的页即可,对应的页不在Buffer Pool中时,才会从磁盘加载对应的页到Buffer Pool,然后再更新,此时Buffer Pool中的页和磁盘中的页数据是不一致的,被称为脏页。这些脏页是要被刷回到磁盘中的

2.脏页刷回磁盘时机

  1. Buffer Pool不够用了,要给新加载的页腾位置了,所以会利用改进的后的LRU算法,将一些脏页刷回磁盘
  2. 后台线程会在MySQL不繁忙的时候,将脏页刷到磁盘中
  3. redolog写满时
  4. 数据库关闭时会将所有脏页刷回到磁盘