1 重做日志(redo log)
redo log又称重做日志文件,用于记录事务操作的变化,记录的是数据修改之后的值,不管事务是否提交都会记录下来。innodb 使用了WAL 技术(Write-Ahead Logging)即先写日志,再写磁盘。 这样可以保证数据的一致性,在实例和介质失败(media failure)时,redo log文件就能派上用场,如数据库掉电,InnoDB存储引擎会使用redo log恢复到掉电前的时刻,以此来保证数据的完整性。
每个InnoDB存储引擎至少有1个重做日志文件组(group),每个文件组至少有2个重做日志文件,redo log 循环使用,如默认的ib_logfile0和ib_logfile1。大小由innodb_log_file_size 参数控制,默认是48M。 文件个数由 innodb_log_files_in_group 参数控制,默认是2个。 文件名由ib_logfile为前缀。
- innodb_log_file_size:指定每个redo日志大小,默认值48MB
- innodb_log_files_in_group:指定日志文件组中redo日志文件数量,默认为2
- innodb_log_group_home_dir:指定日志文件组所在路劲,默认值./,指mysql的数据目录datadir
[dave@www.cndba.cn mysql]# pwd
/var/lib/mysql
[dave@www.cndba.cn mysql]# ll -lh|grep ib_logfile
-rw-r----- 1 root root 48M Aug 21 13:32 ib_logfile0
-rw-r----- 1 root root 48M Aug 20 03:13 ib_logfile1
[dave@www.cndba.cn mysql]#
mysql> show variables like 'innodb_log%';
+-----------------------------+----------+
| Variable_name | Value |
+-----------------------------+----------+
| innodb_log_buffer_size | 16777216 |
| innodb_log_checksums | ON |
| innodb_log_compressed_pages | ON |
| innodb_log_file_size | 50331648 |
| innodb_log_files_in_group | 2 |
| innodb_log_group_home_dir | ./ |
| innodb_log_write_ahead_size | 8192 |
+-----------------------------+----------+
7 rows in set (0.01 sec)
2 二进制日志(binlog)
在之前的博客,我们了解到MySQL 体系架构有2个层次,如下:
MySQL Server 层 和 InnoDB 引擎层 体系结构
https://www.cndba.cn/dave/article/4671
- Server 层:主要做的是 MySQL 功能层面的事情;
- 引擎层: 负责存储相关的具体事宜。
redo log 是 InnoDB 引擎特有的日志,而 Server 层也有自己的日志,称为 binlog(二进制日志)。
binlog记录了对MySQL数据库执行更改的所有操作,但是不包括SELECT和SHOW这类操作,如果想记录SELECT和SHOW操作,那只能使用查询日志—general_log[={0|1}] (1为启用)。
binlog 属于逻辑日志,是以二进制的形式记录的是这个语句的原始逻辑。从 5.1.8 版本开始,二进制日志 (binlog) 有 3 种不同的格式可选:Mixed,Statement,Row,默认格式是 Statement,而从5.7.7版本以上默认是ROW模式。
MySQL binlog日志格式 binlog_format
https://www.cndba.cn/dave/article/4664
mysql> show variables like '%log_bin%';
+---------------------------------+--------------------------------+
| Variable_name | Value |
+---------------------------------+--------------------------------+
| log_bin | ON |
| log_bin_basename | /var/lib/mysql/mysql-bin |
| log_bin_index | /var/lib/mysql/mysql-bin.index |
| log_bin_trust_function_creators | OFF |
| log_bin_use_v1_row_events | OFF |
| sql_log_bin | ON |
+---------------------------------+--------------------------------+
6 rows in set (0.01 sec)
mysql> show variables like '%binlog%';
+--------------------------------------------+----------------------+
| Variable_name | Value |
+--------------------------------------------+----------------------+
| binlog_cache_size | 32768 |
| binlog_checksum | CRC32 |
| binlog_direct_non_transactional_updates | OFF |
| binlog_error_action | ABORT_SERVER |
| binlog_format | MIXED |
| binlog_group_commit_sync_delay | 0 |
| binlog_group_commit_sync_no_delay_count | 0 |
| binlog_gtid_simple_recovery | ON |
| binlog_max_flush_queue_time | 0 |
| binlog_order_commits | ON |
| binlog_row_image | FULL |
| binlog_rows_query_log_events | OFF |
| binlog_stmt_cache_size | 32768 |
| binlog_transaction_dependency_history_size | 25000 |
| binlog_transaction_dependency_tracking | COMMIT_ORDER |
| innodb_api_enable_binlog | OFF |
| innodb_locks_unsafe_for_binlog | OFF |
| log_statements_unsafe_for_binlog | ON |
| max_binlog_cache_size | 18446744073709547520 |
| max_binlog_size | 1073741824 |
| max_binlog_stmt_cache_size | 18446744073709547520 |
| sync_binlog | 1 |
+--------------------------------------------+----------------------+
22 rows in set (0.00 sec)
mysql>
[dave@www.cndba.cn mysql]# ll -lh|grep mysql-bin
-rw-r----- 1 root root 177 Oct 26 2019 mysql-bin.000001
-rw-r----- 1 root root 1.1G Dec 8 2020 mysql-bin.000002
-rw-r----- 1 root root 528M Aug 21 14:05 mysql-bin.000003
-rw-r----- 1 root root 57 Dec 8 2020 mysql-bin.index
[dave@www.cndba.cn mysql]#
redo log与binlog的区别
- redo log是在InnoDB存储引擎层产生,而binlog是MySQL数据库的上层产生的,并且二进制日志不仅仅针对INNODB存储引擎,MySQL数据库中的任何存储引擎对于数据库的更改都会产生二进制日志。
- 两种日志记录的内容形式不同。MySQL的binlog是逻辑日志,其记录是对应的SQL语句。而innodb存储引擎层面的重做日志是物理日志,记录的是在某个数据页上做了什么修改。
- 两种日志与记录写入磁盘的时间点不同,二进制日志只在事务提交完成后进行一次写入。而innodb存储引擎的重做日志在事务进行中不断地被写入,并日志不是随事务提交的顺序进行写入的。
- 二进制日志仅在事务提交时记录,并且对于每一个事务,仅在事务提交时记录,并且对于每一个事务,仅包含对应事务的一个日志。而对于innodb存储引擎的重做日志,由于其记录是物理操作日志,因此每个事务对应多个日志条目,并且事务的重做日志写入是并发的,并非在事务提交时写入,其在文件中记录的顺序并非是事务开始的顺序。
- binlog不是循环使用,在写满或者重启之后,会生成新的binlog文件,redo log是循环使用。
- binlog可以作为恢复数据使用,主从复制搭建,redo log作为异常宕机或者介质故障后的数据恢复使用。
binlog有固定的格式:
- statement 格式的 binlog,最后会有 COMMIT
- row 格式的 binlog,最后会有一个 XID event
从MySQL 5.6.2开始,引入了 binlog_checksum参数,用来验证 binlog 内容的正确性。对于 binlog 日志由于磁盘原因,可能会在日志中间出错的情况,MySQL 可以通过校验 checksum 的结果来发现,保证了事务 binlog 的完整性。
3 redo log 和 binlog 的数据一致性:两阶段提交(2PC)
MySQL 使用两阶段提交主要解决 binlog 和 redo log 的数据一致性的问题。
redo log 和 binlog 都可以用于表示事务的提交状态,而两阶段提交就是让这两个状态保持逻辑上的一致。
两阶段提交过程如下:
- InnoDB redo log 写盘,完成后InnoDB 事务进入 prepare 状态。
- 如果前面 prepare 成功,那么开始binlog 写盘,将事务日志持久化到 binlog,如果持久化成功,那么 InnoDB 事务则进入 commit 状态(在 redo log 里面写一个 commit 记录),事务操作完成。如果有大量长事务在执行,那么这里就可能出现binlog 写入的等待,导致事务无法及时提交而出现阻塞。
每个事务 binlog 的末尾,会记录一个 XID event,标志着事务是否提交成功,也就是说,recovery 过程中,binlog 最后一个 XID event 之后的内容都应该被 purge。
在redo log 和 binlog 文件中有一个共同的数据字段:XID。崩溃恢复的时候,会按顺序扫描 redo log:
- 如果碰到既有 prepare、又有 commit 的 redo log,就直接提交;
- 如果碰到只有 parepare、而没有 commit 的 redo log,就拿着 XID 去 binlog 找对应的事务,进行回滚。