目录
写在最前:
一、初始上三宗
二、小谈上三宗
1、重做日志(redo log)
① redo log 原理
② redo log 作用
③ redo log 内容
④ redo log 什么时候产生❓
⑤ redo log 什么时候释放❓
⑥ redo log 对应的物理文件
⑦ 其它
2、回滚日志(undo log)
① undo log 原理
② undo log 作用
③ undo log 内容
④ undo log 什么时候产生❓
⑤ undo log 什么时候释放❓
⑥ undo log 对应的物理文件
⑦ 其它
3、二进制日志(binlog)
① binlog 作用
② 主从同步原理
③ binlog 内容
④ binlog 什么时候产生❓
⑤ binlog 什么时候释放❓
⑥ binlog 对应的物理文件
⑦ 其它
三、轻描淡写下四宗
1、错误日志(error log)
2、慢查询日志(slow query log)
3、一般查询日志(general log)
4、中继日志(relay log)
① relay log 作用
② relay log 相关参数
③ relay log 内容
④ relay log 什么时候产生❓
⑤ relay log 什么时候释放❓
日志文件是 MySQL 数据库的重要组成部分。MySQL 有几种不同的日志文件,通常包括重做日志、回滚日志、二进制日志、错误日志、慢查询日志、一般查询日志、中继日志等。这些日志可以帮助我们定位 MySQL 内部发生的事件,MySQL 性能故障,记录数据的变更历史,恢复数据等。
其中重做日志和回滚日志与事务息息相关,二进制日志也与事务操作有一定的关系,这三种日志,对理解 MySQL 中的事务操作有着重要意义。所以接下来我称之为上三宗(O(∩_∩)O哈哈~斗罗大陆看多了)
1、redo log:是为了持久化数据,在数据还没从内存刷新到磁盘时,如果发生故障,可读取 redo log 持久化到磁盘。
2、bin log:是为了复制和恢复数据的,即 MySQL 从服务器可以读取主服务器的 binlog 复制数据,数据库数据丢失,可以读取 binlog 恢复。
3、undo log:是为了保证事务的原子性的。在 InnoDB 存储引擎中,还用 undo log 来实现多版本并发控制(MVCC)。
redo log 记录的是新数据的备份。在事务提交前,只要将 redo log 持久化即可,不需要将数据化持久化到磁盘,当系统崩溃时,虽然数据还没持久化到磁盘,但 redo log 已经持久化,系统可以根据 redo log 的内容,将所有数据恢复到最新的状态。
redo log 是为了实现事务的持久性而出现的产物。
确保事务的持久化。redo log 记录事务执行后的状态,用来恢复未持久化到磁盘的已成功事务更新的数据,防止在发生故障的时间点,尚有脏页未持久化到磁盘,在重启 MySQL 服务的时候,根据 redo log 进行重做,从而达到事务持久性这一特性。
物理格式的日志,记录的事务物理·数据页面的修改信息,其 redo log 是顺序写入 redo log file 的物理文件(ib_logfileN)中去的。
事务开始之后就产生了 redo log,redo log 的落盘并不是随着事务的提交才写入的,而是在事务的执行过程中,便开始写入 redo log 文件中,即随着事务的开始,逐步开始落盘。
当对应事务的脏页写入到磁盘之后,redo log 的使命也就完成了,重做日志占用的空间就可以重用(被覆盖)。
默认情况下,对应的物理文件位于数据库的 data 目录下的 ib_logfile1 & ib_logfile2。
关于文件的大小和数量,由以下两个参数配置:
很重要的一点,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 到磁盘:
为了满足事务的原子性,在操作任何数据之前,首先将数据备份存储到 undo log,然后进行数据修改,如果出现错误或者用户执行了 rollback 语句,系统可以利用 undo log 中备份的数据恢复到事务开始执行前的状态。
保证数据的原子性,保存了事务发生之前数据的一个版本,可以用于回滚,同时可以提供多版本并发控制下的读(MVCC),即非锁定读。
逻辑格式的日志,在执行 undo log 的时候,仅仅是将数据从逻辑上恢复至事务之前的状态,而不是从物理页面上操作实现的,这一点是不同于 redo log 的。
事务开始之前,将当前的版本生成 undo log,undo 也会产生 redo 来保证 undo log 的可靠性。
当事务提交后,undo log 并不能立马被删除,而是放入待清理的链表,由 purge 线程判断是否有其它事务在使用 undo 段中表的上一个事务之前的版本信息,决定是否可以清理 undo log 的日志空间。
如果 undo 使用的共享表空间,这个共享表空间中又不仅仅是存储 undo 的信息,共享表空间默认在 MySQL 的数据目录下面,其属性由参数 innodb_data_file_path 配置。
关于 MySQL5.7 之后的独立 undo 表空间配置参数请进一步查看官方文档。
undo 是在事务开始之前保存的被修改数据的一个版本,产生 undo log 的时候,同样会伴随类似于保护事务持久化机制的 redo log 的产生。
默认情况下 undo 文件是保存在共享表空间的,即 ibdatafile 文件中,当数据库中发生一些大的事务性操作的时候,要生成大量的 undo 信息,全部保存在共享表空间中。因此共享表空间可能会变的很大,也就是 undo log 使用共享表空间的时候,被“撑大”的共享表空间是不会也不能自动收缩的。
因此,MySQL5.7 之后的“独立 undo 表空间”的配置就显得很有必要了。
用于复制,在主从复制中,从库利用主库上的 binlog 进行重播,实现主从同步。
主服务器把数据更新记录到二进制日志中并通知从服务器同步,从服务器收到通知后通过 I/O 线程向主库发起 binlog 请求,主服务器通过 dump 线程把二进制日志传送给从服务器,从服务器通过 I/O 线程记录到自己的中继日志中,然后在通过 SQL 线程解析应用中继日志中的内容,完成同步。
从库在主从之间建立长连接,主从同步需要三个线程:master(binlog dump thread)、slave(I/O thread、SQL thread):
逻辑格式的日志,可以简单认为就是执行过的事务中的 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 |
+---------------+-------+
事务提交的时候,一次性将事务中的 SQL 语句按照一定的格式记录到 binlog 中。
这里与 redo log 很明显的差异就是 redo log 并不一定是在事务提交的时候刷新到磁盘,redo log 是在事务开始之后就逐步写入磁盘,因此对于事务的提交,即便是较大的事务,提交(commit)都是很快的。
binlog 的默认保存时间由参数 expire_logs_days 配置,也就是说对于非活动的日志文件,在生产时间超过 expire_logs_days 配置的天数之后,就会被自动删除。
mysql> show variables like 'expire_logs_days';
+------------------+-------+
| Variable_name | Value |
+------------------+-------+
| expire_logs_days | 30 |
+------------------+-------+
物理文件的路径为 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 很类似,但是亮着有本质的不同,可不要混淆了:
在 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 |
+---------------+-------+
慢查询日志是用来记录执行时间超过指定时间的查询语句。通过慢查询日志,可以查找出哪些查询语句的执行效率很低,以便进行优化。一般建议开启,它对服务器性能影响很小,但是可以记录 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 |
+---------------------+-------------------------------+
默认情况,一般查询日志是关闭的。因为查询日志会记录用户所有的操作,其中还包括增删改查等信息,如果在高并发的环境下会产生大量的信息,导致不必要的磁盘 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;
从服务器 I/O thread 将主服务器的二进制日志读取过来记录到从服务器本地文件(即 relay log),然后从服务器 SQL thread 会读取 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 参数一样。
逻辑格式日志。
主服务器 dump thread 将 binlog 传送给从服务器 I/O thread,从服务器 I/O thread 将请求回来 binlog 存到本地 relay log 中。
relay log 同步完成完成后就会被系统自动删除。