目录
写在最前:
日志文件是 MySQL 数据库的重要组成部分。MySQL 有几种不同的日志文件,通常包括重做日志、回滚日志、二进制日志、错误日志、慢查询日志、一般查询日志、中继日志等。这些日志可以帮助我们定位 MySQL 内部发生的事件,MySQL 性能故障,记录数据的变更历史,恢复数据等。
- 重做日志(redo log),又被称为 Innodb 的在线 redo 日志(innodb redo log)
- 回滚日志(undo log)
- 二进制日志(bin log)
- 错误日志(error log)
- 慢查询日志(slow query log)
- 一般查询日志(general log)
- 中继日志(relay log)
其中重做日志和回滚日志与事务息息相关,二进制日志也与事务操作有一定的关系,这三种日志,对理解 MySQL 中的事务操作有着重要意义。所以接下来我称之为上三宗(O(∩_∩)O哈哈~斗罗大陆看多了)
一、初始上三宗
1、redo log:是为了持久化数据,在数据还没从内存刷新到磁盘时,如果发生故障,可读取 redo log 持久化到磁盘。
2、bin log:是为了复制和恢复数据的,即 MySQL 从服务器可以读取主服务器的 binlog 复制数据,数据库数据丢失,可以读取 binlog 恢复。
3、undo log:是为了保证事务的原子性的。在 InnoDB 存储引擎中,还用 undo log 来实现多版本并发控制(MVCC)。
二、小谈上三宗
1、重做日志(redo log)
① redo log 原理
redo log 记录的是新数据的备份。在事务提交前,只要将 redo log 持久化即可,不需要将数据化持久化到磁盘,当系统崩溃时,虽然数据还没持久化到磁盘,但 redo log 已经持久化,系统可以根据 redo log 的内容,将所有数据恢复到最新的状态。
redo log 是为了实现事务的持久性而出现的产物。
② redo log 作用
确保事务的持久化。redo log 记录事务执行后的状态,用来恢复未持久化到磁盘的已成功事务更新的数据,防止在发生故障的时间点,尚有脏页未持久化到磁盘,在重启 MySQL 服务的时候,根据 redo log 进行重做,从而达到事务持久性这一特性。
③ redo log 内容
物理格式的日志,记录的事务物理·数据页面的修改信息,其 redo log 是顺序写入 redo log file 的物理文件(ib_logfileN)中去的。
④ redo log 什么时候产生❓
事务开始之后就产生了 redo log,redo log 的落盘并不是随着事务的提交才写入的,而是在事务的执行过程中,便开始写入 redo log 文件中,即随着事务的开始,逐步开始落盘。
⑤ redo log 什么时候释放❓
当对应事务的脏页写入到磁盘之后,redo log 的使命也就完成了,重做日志占用的空间就可以重用(被覆盖)。
⑥ redo log 对应的物理文件
默认情况下,对应的物理文件位于数据库的 data 目录下的 ib_logfile1 & ib_logfile2。
- innodb_log_group_home_dir 指定日志文件组所在的路径,默认 ./ ,表示在数据库的数据目录下。
- innodb_log_files_in _group 指定重做日志文件组中文件的数量,默认为 2。
关于文件的大小和数量,由以下两个参数配置:
- innodb_log_file_size 重做日志文件的大小。
- innodb_mirrored_log_groups 指定了日志镜像文件组的数量,默认 1。
⑦ 其它
很重要的一点,redo log 是什么时候写盘的?前面 ④ 中也说了是在事务开始之后逐步写盘的。
之所以说重做日志是在事务开始之后逐步写入重做日志文件,而不一定是事务提交才写入重做日志缓存,原因就是重做日志有一个缓存区 Innodb_log_buffer,Innodb_log_buffer 默认大小为 8M(这里设置为 96M),Innodb 存储引擎先将重做日志写入 Innodb_log_buffer 中。
mysql> show variables like 'innodb_log_buffer%';
+------------------------+-----------+
| Variable_name | Value |
+------------------------+-----------+
| innodb_log_buffer_size | 100663296 |
+------------------------+-----------+
mysql> select (100663296/1024/1024);
+-----------------------+
| (100663296/1024/1024) |
+-----------------------+
| 96.00000000 |
+-----------------------+
《MySQL 技术内幕 Innodb 存储引擎》(page 37):即使某个事务还没有提交,Innodb 存储引擎仍然每秒会将重做日志缓存刷新到重做日志文件。
这一点必须要知道的,因为这可以很好的解释再大的事务提交,时间也是很短暂的。
redo log 包括两部分:一是内存中的日志缓冲(redo log buffer),该部分日志是易失性的;二是磁盘上的重做日志文件(redo log file),该部分日志是持久的。
在概念上,innodb 通过 force log at commit 机制实现事务的持久性,即在事务提交的时候,必须先将该事务的所有事务日志写入到磁盘上的 redo log file 和 undo log file 中进行持久化。
过程如下:
为了确保每次日志都能写入到事务日志文件中,在每次将 log buffer 中的日志写入日志文件的过程中都会调用一次操作系统的 fsync 操作(即 fsync() 系统调用)。因为 MySQL 是工作在用户空间的,MySQL 的 log buffer 处于用户空间的内存中。要写入到磁盘上的 log file 中(redo:ib_logfileN 文件,undo:share tablespace 或 .ibd 文件),中间还要经过操作系统内核空间的 OS Buffer,调用 fsync() 的作用就是将 OS Buffer 中的日志刷到磁盘上的 log file 中。
MySQL 支持用户自定义在 commit 时如何将 log buffer 中的日志刷到 log file 中。
这种控制通过参数 innodb_flush_log_at_trx_commit 的值来决定。该参数有 3 个值:0、1、2,默认为 1。
但注意,这个参数只是控制 commit 动作是否刷新 log buffer 到磁盘:
- 当设置为 1 的时候,事务每次提交都会将 log buffer 中的日志写入 os buffer 并调用 fsync() 刷到 log file on disk 中。这种方式即使系统崩溃也不会丢失任何数据,但是因为每次提交都写入磁盘,IO 的性能较差;
- 当设置为 0 的时候,事务提交时不会将 log buffer 中日志写入到 os buffer,而是每秒写入 os buffer 并调用 fsync() 写入到 log file on disk 中。也就是说设置为 0 时是(大约)每秒刷新写入到磁盘中的,当系统崩溃,会丢失 1 秒钟的数据;
- 当设置为 2 的时候,每次提交都仅写入到 os buffer,然后是每秒调用 fsync() 将 os buffer 中的日志写入到 log file on disk。
2、回滚日志(undo log)
① undo log 原理
为了满足事务的原子性,在操作任何数据之前,首先将数据备份存储到 undo log,然后进行数据修改,如果出现错误或者用户执行了 rollback 语句,系统可以利用 undo log 中备份的数据恢复到事务开始执行前的状态。
② undo log 作用
保证数据的原子性,保存了事务发生之前数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),即非锁定读。
③ undo log 内容
逻辑格式的日志,在执行 undo log 的时候,仅仅是将数据从逻辑上恢复至事务之前的状态,而不是从物理页面上操作实现的,这一点是不同于 redo log 的。
④ undo log 什么时候产生❓
事务开始之前,将当前的版本生成 undo log,undo 也会产生 redo 来保证 undo log 的可靠性。
⑤ undo log 什么时候释放❓
当事务提交后,undo log 并不能立马被删除,而是放入待清理的链表,由 purge 线程判断是否有其它事务在使用 undo 段中表的上一个事务之前的版本信息,决定是否可以清理 undo log 的日志空间。
⑥ undo log 对应的物理文件
如果 undo 使用的共享表空间,这个共享表空间中又不仅仅是存储 undo 的信息,共享表空间默认在 MySQL 的数据目录下面,其属性由参数 innodb_data_file_path 配置。
- MySQL5.6 之前,undo 表空间位于共享表空间的回滚段中,共享表空间默认的名称是 ibdata(.ibd 文件),位于数据文件目录中;
- MySQL5.6 之后,undo 表空间可以配置成独立的文件,但是提前需要在配置文件中配置,完成数据库初始化后生效且不可改变 undo log 文件的个数;
- 如果初始化数据库之前没有进行相关配置,那么就无法配置成独立的表空间了。
关于 MySQL5.7 之后的独立 undo 表空间配置参数请进一步查看官方文档。
- innodb_undo_directory = /data/undospace/ # undo 独立表空间的存放目录
- innodb_undo_log = 128 # 回滚段为 128KB
- innodb_undo_tablespaces = 4 # 指定有 4 个 undo log 文件
⑦ 其它
undo 是在事务开始之前保存的被修改数据的一个版本,产生 undo log 的时候,同样会伴随类似于保护事务持久化机制的 redo log 的产生。
默认情况下 undo 文件是保存在共享表空间的,即 ibdatafile 文件中,当数据库中发生一些大的事务性操作的时候,要生成大量的 undo 信息,全部保存在共享表空间中。因此共享表空间可能会变的很大,也就是 undo log 使用共享表空间的时候,被“撑大”的共享表空间是不会也不能自动收缩的。
因此,MySQL5.7 之后的“独立 undo 表空间”的配置就显得很有必要了。
3、二进制日志(binlog)
① binlog 作用
用于复制,在主从复制中,从库利用主库上的 binlog 进行重播,实现主从同步。
② 主从同步原理
主服务器把数据更新记录到二进制日志中并通知从服务器同步,从服务器收到通知后通过 I/O 线程向主库发起 binlog 请求,主服务器通过 dump 线程把二进制日志传送给从服务器,从服务器通过 I/O 线程记录到自己的中继日志中,然后在通过 SQL 线程解析应用中继日志中的内容,完成同步。
从库在主从之间建立长连接,主从同步需要三个线程:master(binlog dump thread)、slave(I/O thread、SQL thread):
- 主库接收到更新命令,执行更新操作,生成 binlog;
- 主服务器创建 dump thread 通知从服务器 I/O thread 有数据更新;
- 从服务器 I/O thread 收到通知后向主服务器 dump thread 发起请求;
- 主服务器 dump thread 收到从服务器 I/O thread 请求内容时会从本地读取 binlog;
- 主服务器 dump thread 将 binlog 传送给从服务器 I/O thread;
- 从服务器 I/O thread 将请求回来 binlog 存到本地 relay log 中;
- 从服务器 SQL thread 检测到 relay log 有更新后,会读取解析 relay log,
- 从服务器 SQL thread 将解析后的内容在本地重新执行一遍,保证主从数据同步。
③ binlog 内容
逻辑格式的日志,可以简单认为就是执行过的事务中的 SQL 语句。
但又不完全是 SQL 语句那么简单,而是包括了执行的 SQL语句反向的信息(也就意味着insert 对应着 insert 本身和 delete 的信息;update 对应着 update 执行前后的版本的信息;delete 对应着 delete 本身和其反向的 insert)。
在使用 mysqlbinlog 解析 binlog 之后就能发现,以下是 mysqlbinlog 几个常用选项:
-d,--database=name:只查看指定数据库的日志操作 -o,--offset=#:忽略掉日志中的前 n 个操作命令。
-r,--result-file=name:将输出的日志信息输出到指定的文件中,使用重定向也一样可以。
-s,--short-form:显示简单格式的日志,只记录一些普通的语句,会省略掉一些额外的信息如位置信息和时间信息以及基于行的日志。可以用来调试,生产环境千万不可使用。
--set-charset=char_name:在输出日志信息到文件中时,在文件第一行加上 set names char_name。
--start-datetime,--stop-datetime:指定输出开始时间和结束时间内的所有日志信息
--start-position=#,--stop-position=#:指定输出开始位置和结束位置内的所有日志信息
-v,-vv:显示更详细信息,基于 row 的日志默认不会显示出来,此时使用 -v 或 -vv 可以查看。
MySQL 的 binlog 有三种格式:statement、row 和 mixed。
mysql> show variables like 'binlog_format';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | MIXED |
+---------------+-------+
④ binlog 什么时候产生❓
事务提交的时候,一次性将事务中的 SQL 语句按照一定的格式记录到 binlog 中。
这里与 redo log 很明显的差异就是 redo log 并不一定是在事务提交的时候刷新到磁盘,redo log 是在事务开始之后就逐步写入磁盘,因此对于事务的提交,即便是较大的事务,提交(commit)都是很快的。
⑤ binlog 什么时候释放❓
binlog 的默认保存时间由参数 expire_logs_days 配置,也就是说对于非活动的日志文件,在生产时间超过 expire_logs_days 配置的天数之后,就会被自动删除。
mysql> show variables like 'expire_logs_days';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| expire_logs_days | 30 |
+------------------+-------+
⑥ binlog 对应的物理文件
物理文件的路径为 log_bin_basename,当日志文件达到指定大小之后进行滚动更新,生成新的日志文件。
对于每个 binlog 文件,通过一个统一的 index 文件来组织。
mysql> show variables like 'log_bin_basename';
+------------------+------------------------------------------------+
| Variable_name | Value |
+------------------+------------------------------------------------+
| log_bin_basename | /data/mysql5.6.16log/mysql3211/binlogs/log_bin |
+------------------+------------------------------------------------+
mysql> select (1073741824/1024/1024/1024);
+-----------------------------+
| (1073741824/1024/1024/1024) |
+-----------------------------+
| 1.000000000000 |
+-----------------------------+
# 1G
mysql> show variables like 'log_bin_index';
+---------------+------------------------------------------------------+
| Variable_name | Value |
+---------------+------------------------------------------------------+
| log_bin_index | /data/mysql5.6.16log/mysql3211/binlogs/log_bin.index |
+---------------+------------------------------------------------------+
## 可以通过查看二进制的 index 文件来查看当前正在使用哪些二进制日志
# cat /data/mysql5.6.16log/mysql3211/binlogs/log_bin.index
## 也可以在 MySQL 环境中使用 show {binary | master} logs 来查看,binary 和 master 是同义词
mysql> show binary logs;
或
mysql> show master logs;
## 查看日志中进行了哪些操作
mysql> show binlog events in 'log_bin.000128';
## 可以指定起始位置,起始位置必须指定正确,不能指定不存在的位置
mysql> show binlog events in 'log_bin.000128' from 1024;
## 删除二进制日志有几种方法,不管哪种方法,都会将删除后的信息同步到二进制 index 文件中
# reset master 将会删除所有日志,并让日志文件重新从 000001 开始
mysql> reset master;
# PURGE { BINARY | MASTER } LOGS { TO 'log_name' | BEFORE datetime_expr }
mysql> purge master logs to "log_bin.000128";
或
mysql> purge binary logs to "log_bin.000128";
# 删除指定日期之前的所有日志,但是若指定的时间处在正在使用中的日志文件中,将无法进行purge
mysql> purge master logs before 'yyyy-mm-dd hh:mi:ss'
MySQL 提供了一个 sync_binlog 参数来控制数据库的 binlog 刷新到磁盘上去。默认 sync_binlog = 0,表示 MySQL 不控制 binlog 的刷新,由文件系统自己控制它的缓存的刷新,这时候性能是最好的,但风险也是最大的。一旦系统 crash,在 binlog_cache 中的所有 binlog 信息会被丢失。
如果 sync_binlog > 0,表示每 sync_binlog 次事物提交,MySQL 调用文件系统的刷新操作将缓存持久化。最安全的就是 sync_binlog = 1,表示每次事物提交,MySQL 都会把 binlog 刷下去,是最安全但性能损耗做大的设置。
mysql> show variables like 'sync_binlog';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| sync_binlog | 600 |
+---------------+-------+
⑦ 其它
二进制日志的作用之一是还原数据库的,这与 redo log 很类似,但是亮着有本质的不同,可不要混淆了:
- 作用不同:redo log 是保证事务的持久性的,是事务层面,binlog 作为还原的功能,是数据库层面的(当然也可以精确到事务层面),虽然都有还原的意思,但是其保护数据的层次是不一样的。
- 内容不同:redo log 是物理日志,是数据页面的修改之后的物理记录,binlog 是逻辑日志(可以简单认为记录的及时 SQL 语句)。
- 另外,两者产生的时间,可以释放的实际,在可释放的情况下清理机制,都是完全不同的。
- 恢复数据时候的效率,基于物理日志的 redo log 恢复数据的效率要高于逻辑日志的 binlog。
三、轻描淡写下四宗
1、错误日志(error log)
在 MySQL 数据库中,错误日志功能是默认开启的,而且无法被关闭。默认情况,错误日志存储在 MySQL 数据库的数据文件中。
错误日志可以自己配置,错误日志可以通过 log_error 和 log_warnings 来定义,其中 log_error:配置是否启用错误日志功能和错误日志的存储位置;log_warning:配置是否将警告信息也定义至错误日志中。
错误日志记录信息:服务器启动关闭信息、运行错误信息、时间调度器运行一个事件时产生的信息、在服务器上启动进程产生的信息。
mysql> show variables like 'log_error';
+---------------+-----------------------------------------+
| Variable_name | Value |
+---------------+-----------------------------------------+
| log_error | /data/mysql5.6.16log/mysqld-5.6-err.log |
+---------------+-----------------------------------------+
mysql> show variables like 'log_warnings';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_warnings | 1 |
+---------------+-------+
2、慢查询日志(slow query log)
慢查询日志是用来记录执行时间超过指定时间的查询语句。通过慢查询日志,可以查找出哪些查询语句的执行效率很低,以便进行优化。一般建议开启,它对服务器性能影响很小,但是可以记录 MySQL 服务器上执行很长时间的查询语句。可以帮助我们定义性能问题。
# 查看慢查询时间
mysql> show variables like 'long_query_time';
+-----------------+----------+
| Variable_name | Value |
+-----------------+----------+
| long_query_time | 1.000000 |
+-----------------+----------+
# 查看慢查询日志路径
mysql> show variables like 'slow_query_log_file';
+---------------------+-------------------------------+
| Variable_name | Value |
+---------------------+-------------------------------+
| slow_query_log_file | /data/mysql5.6.16log/slow.log |
+---------------------+-------------------------------+
3、一般查询日志(general log)
默认情况,一般查询日志是关闭的。因为查询日志会记录用户所有的操作,其中还包括增删改查等信息,如果在高并发的环境下会产生大量的信息,导致不必要的磁盘 IO,会影响 MySQL 的性能。
使用 general_log = {0 | 1} 来决定是否启用一般查询日志,使用 general_log_file = file_name 来指定查询日志的路径,不给定路径时默认的文件名以 {hostname}.log 命名。
mysql> show variables like 'general_log%';
+------------------+----------------------------------------------------------------+
| Variable_name | Value |
+------------------+----------------------------------------------------------------+
| general_log | OFF |
| general_log_file | /data/mysql5.6.16data/mysql3211/data/yz-higo-accountdb-m01.log |
+------------------+----------------------------------------------------------------+
## 是否启用一般查询日志,为全局变量,必须在global上修改
mysql> set @@global.general_log=1;
4、中继日志(relay log)
① relay log 作用
从服务器 I/O thread 将主服务器的二进制日志读取过来记录到从服务器本地文件(即 relay log),然后从服务器 SQL thread 会读取 relay log 的内容并应用到从服务器,从而使从服务器和主服务器的数据保持一致。
② relay log 相关参数
mysql> show variables like '%relay%';
+---------------------------+----------------------------------------------------------+
| Variable_name | Value |
+---------------------------+----------------------------------------------------------+
| max_relay_log_size | 0 |
| relay_log | /data/mysql5.6.16log/mysql3211/relaylogs/relay-bin |
| relay_log_basename | /data/mysql5.6.16log/mysql3211/relaylogs/relay-bin |
| relay_log_index | /data/mysql5.6.16log/mysql3211/relaylogs/relay-bin.index |
| relay_log_info_file | relay-log.info |
| relay_log_info_repository | FILE |
| relay_log_purge | ON |
| relay_log_recovery | OFF |
| relay_log_space_limit | 0 |
| sync_relay_log | 10000 |
| sync_relay_log_info | 10000 |
+---------------------------+----------------------------------------------------------+
# max_relay_log_size:relay log 允许的最大值,如果该值为 0,则默认值为 max_binlog_size (1G);如果不为 0,则 max_relay_log_size 则为最大的 relay_log 文件大小。
# relay_log:定义 relay_log 的位置和名称,如果值为空,则默认位置在数据文件的目录。
# relay_log_info_file:定义 relay-log.info 的位置和名称,relay-log.info 记录 master 主库的 binary_log 的恢复位置和 从库 relay_log 的位置。
# sync_relay_log:当设置为 1 时,slave 的 I/O thread 每次接收到 master 发送过来的 binlog 日志都要写入系统缓冲区,然后刷入 relay log 中继日志里,这样是最安全的,因为在崩溃的时候,你最多会丢失一个事务,但会造成磁盘的大量 I/O;当设置为 0 时,并不是马上就刷入中继日志里,而是由操作系统决定何时来写入,虽然安全性降低了,但减少了大量的磁盘 I/O 操作。这个值默认是 0,可动态修改。
# sync_relay_log_info:这个参数和 sync_relay_log 参数一样。
③ relay log 内容
逻辑格式日志。
④ relay log 什么时候产生❓
主服务器 dump thread 将 binlog 传送给从服务器 I/O thread,从服务器 I/O thread 将请求回来 binlog 存到本地 relay log 中。
⑤ relay log 什么时候释放❓
relay log 同步完成完成后就会被系统自动删除。