三种日志文件
- binlog 二进制日志
- redo log 重做日志
- 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 语句时的内部流程
- 执行器先找引擎取 ID=2 这一行。ID 是主键,引擎直接用树搜索找到这一行。如果 ID=2 这一行所在的数据页本来就在内存中,就直接返回给执行器;否则,需要先从磁盘读入内存,然后再返回。
- 执行器拿到引擎给的行数据,把这个值加上 1,比如原来是 N,现在就是 N+1,得到新的一行数据,再调用引擎接口写入这行新数据。
- 引擎将这行新数据更新到内存(InnoDB Buffer Pool)中,同时将这个更新操作记录到 redo log 里面,此时 redo log buffer 处于 prepare 状态。然后告知执行器执行完成了,随时可以提交事务。
- 执行器生成这个操作的 binlog,并把 binlog 写入磁盘。
- 执行器调用引擎的提交事务接口,引擎把刚刚写入的 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.脏页刷回磁盘时机
- Buffer Pool不够用了,要给新加载的页腾位置了,所以会利用改进的后的LRU算法,将一些脏页刷回磁盘
- 后台线程会在MySQL不繁忙的时候,将脏页刷到磁盘中
- redolog写满时
- 数据库关闭时会将所有脏页刷回到磁盘