MySQL 日志:常见的日志都有什么用?
MySQL 日志的内容非常重要,面试中经常会被问到。同时,掌握日志相关的知识也有利于我们理解 MySQL 底层原理,必要时帮助我们排查解决问题。
MySQL 中常见的日志类型主要有下面几类(针对的是 InnoDB 存储引擎):
- 错误日志(error log) :对 MySQL 的启动、运行、关闭过程进行了记录。
- 二进制日志(binary log,binlog) :主要记录的是更改数据库数据的 SQL 语句。
- 一般查询日志(general query log) :已建立连接的客户端发送给 MySQL 服务器的所有 SQL 记录,因为 SQL 的量比较大,默认是不开启的,也不建议开启。
- 慢查询日志(slow query log) :执行时间超过
long_query_time秒钟的查询,解决 SQL 慢查询问题的时候会用到。 - 事务日志(redo log 和 undo log) :redo log 是重做日志,undo log 是回滚日志。
- 中继日志(relay log) :relay log 是复制过程中产生的日志,很多方面都跟 binary log 差不多。不过,relay log 针对的是主从复制中的从库。
- DDL 日志(metadata log) :DDL 语句执行的元数据操作。
二进制日志(binlog)和事务日志(redo log 和 undo log)比较重要,需要我们重点关注。
slow query log(慢查询日志)
慢查询日志有什么用?
慢查询日志记录了执行时间超过 long_query_time(默认是 10s,通常设置为1s)的所有查询语句,在解决 SQL 慢查询(SQL 执行时间过长)问题的时候经常会用到。
找到慢 SQL 是优化 SQL 语句性能的第一步,然后再用EXPLAIN 命令可以对慢 SQL 进行分析,获取执行计划的相关信息。
你可以通过 show variables like "slow_query_log";命令来查看慢查询日志是否开启,默认是关闭的。
mysql> show variables like 'slow_query_log';
+----------------+-------+
| Variable_name | Value |
+----------------+-------+
| slow_query_log | OFF |
+----------------+-------+
你可以通过 SET GLOBAL slow_query_log=ON 命令将其开启。
long_query_time 参数定义了一个查询消耗多长时间才可以被定义为慢查询,默认是 10s,通过 SHOW VARIABLES LIKE '%long_query_time%'命令即可查看:
mysql> show variables like '%long_query_time%';
+-----------------+-----------+
| Variable_name | Value |
+-----------------+-----------+
| long_query_time | 10.000000 |
+-----------------+-----------+
1 row in set (0.00 sec)
并且,我们还可以对 long_query_time 参数进行修改:
SET GLOBAL long_query_time=1
在实际项目中,慢查询日志可能会比较大,直接分析的话不太方便,我们可以借助 MySQL 官方的慢查询分析调优工具 mysqldumpslow。
如何查询当前慢查询语句的个数?
在MySQL中有一个变量专门记录当前慢查询语句的个数,可以通过 show global status like '%Slow_queries%'; 命令查看。
mysql> show global status like '%Slow_queries%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| Slow_queries | 10 |
+---------------+-------+
如何优化慢 SQL?
MySQL 为我们提供了 EXPLAIN 命令,来获取执行计划的相关信息。
执行计划 是指一条 SQL 语句在经过 MySQL 查询优化器 的优化会后,具体的执行方式。执行计划通常用于 SQL 性能分析、优化等场景。通过 EXPLAIN 的结果,可以了解到如数据表的查询顺序、数据查询操作的操作类型、哪些索引可以被命中、哪些索引实际会命中、每个数据表有多少行记录被查询等信息。
另外,为了更精准定位一条 SQL 语句的性能问题,需要清楚地知道这条 SQL 语句运行时消耗了多少系统资源。 [SHOW PROFILE](https://dev.mysql.com/doc/refman/5.7/en/show-profile.html) 和 [SHOW PROFILES](https://dev.mysql.com/doc/refman/5.7/en/show-profiles.html) 展示 SQL 语句的资源使用情况,展示的消息包括 CPU 的使用,CPU 上下文切换,IO 等待,内存使用等。
详细介绍可以查看这篇文章:
binlog(二进制日志)
binlog 是什么?
binlog(binary log 即二进制日志文件) 主要记录了对 MySQL 数据库执行了更改的所有操作(数据库执行的所有 DDL 和 DML 语句),包括表结构变更(CREATE、ALTER、DROP TABLE…)、表数据修改(INSERT、UPDATE、DELETE...),但不包括 SELECT、SHOW 这类不会对数据库造成更改的操作。
不过,并不是不对数据库造成修改就不会被记录进 binlog。即使表结构变更和表数据修改操作并未对数据库造成更改,依然会被记录进 binlog。
你可以使用show binary logs; 命令查看所有二进制日志列表:
mysql> show binary logs;
+---------------+-----------+-----------+
| Log_name | File_size | Encrypted |
+---------------+-----------+-----------+
| binlog.000007 | 619853 | No |
| binlog.000008 | 49104 | No |
+---------------+-----------+-----------+
可以看到,binlog 日志文件名为 文件名.00000* 形式。
你可以通过 show binlog events in 'binlog.000008' limit 10; 命令查看日志的具体内容。这里一定要指定 limit,不然查询出来的日志文件内容太多。另外, MySQL 内置了 binlog 查看工具 mysqlbinlog,可以解析二进制文件。
binlog 通过追加的方式进行写入,大小没有限制。并且,我们可以通过max_binlog_size参数设置每个 binlog 文件的最大容量,当文件大小达到给定值之后,会生成新的 binlog 文件来保存日志,不会出现前面写的日志被覆盖的情况。
binlog 的格式有哪几种?
一共有 3 种类型二进制记录方式:
- Statement 模式 :每一条会修改数据的sql都会被记录在binlog中,如inserts, updates, deletes。
- Row 模式 (推荐): 每一行的具体变更事件都会被记录在binlog中。
- Mixed 模式 :Statement 模式和 Row 模式的混合。默认使用 Statement 模式,少数特殊具体场景自动切换到 Row 模式。
MySQL 5.1.5 之前 binlog 的格式只有 STATEMENT,5.1.5 开始支持 ROW 格式的 binlog,从 5.1.8 版本开始,MySQL 开始支持 MIXED 格式的 binlog。MySQL 5.7.7 之前,默认使用 Statement 模式。MySQL 5.7.7 开始默认使用 Row 模式。
相比较于 Row 模式来说,Statement 模式下的日志文件更小,磁盘 IO 压力也较小,性能更好有些。不过,其准确性相比于 Row 模式要差。
你可以使用 show variables like '%binlog_format%'; 查看 binlog 使用的格式:
Server version: 8.0.27 MySQL Community Server - GPL
mysql> show variables like '%binlog_format%';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW |
+---------------+-------+
1 row in set (0.05 sec)
binlog 主要用来做什么?
binlog 最主要的应用场景是 主从复制 ,主备、主主、主从都离不开binlog,需要依靠 binlog 来同步数据,保证数据一致性。
主从复制的原理如下图所示:

- 主库将数据库中数据的变化写入到 binlog
- 从库连接主库
- 从库会创建一个 I/O 线程向主库请求更新的 binlog
- 主库会创建一个 binlog dump 线程来发送 binlog ,从库中的 I/O 线程负责接收
- 从库的 I/O 线程将接收的 binlog 写入到 relay log 中。
- 从库的 SQL 线程读取 relay log 同步数据本地(也就是再执行一遍 SQL )。
关于主从复制的相关内容,推荐看看我写的数据库读写分离和分库分表详解这篇文章。
另外,常见的一些同步 MySQL 数据到其他数据源的工具(比如 Canal)的底层一般也是依赖 binlog 。
Canal 数据同步的原理如下图所示:

- Canal 模拟 MySQL Slave 节点与 MySQL Master 节点的交互协议,把自己伪装成一个 MySQL Slave 节点,向 MySQL Master 节点请求 binlog;
- MySQL Master 节点接收到请求之后,根据偏移量将新的 binlog 发送给 MySQL Slave 节点;
- Canal 接收到 binlog 之后,就可以对这部分日志进行解析,获取主库的结构及数据变更。
除了主从复制之外,binlog 还能帮助我们实现 数据恢复 。当我们误删数据甚至是整个数据库的情况下,就可以使用 binlog 来帮助我们恢复数据。当然了,大前提是已经启用了 binlog 日志。
你可以使用show variables like 'log_bin';查看数据库是否启用 binlog 日志,默认是开启的。
mysql> show variables like 'log_bin';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | ON |
+---------------+-------+
binlog 的刷盘时机如何选择?
对于InnoDB存储引擎而言,事务在执行过程中,会先把日志写入到binlog cache中,只有在事务提交的时候,才会把binlog cache中的日志持久化到磁盘上的binlog文件中。写入内存的速度更快,这样做也是为了效率考虑。
因为一个事务的binlog不能被拆开,无论这个事务多大,也要确保一次性写入,所以系统会给每个线程分配一个块内存作为binlog cache。我们可以通过binlog_cache_size参数控制单个线程 binlog cache 大小,如果存储内容超过了这个参数,就要暂存到磁盘(Swap)。
那么 binlog 是什么时候刷到磁盘中的呢? 可以通过 sync_binlog 参数控制 biglog 的刷盘时机,取值范围是 0-N,默认为 0 :
- 0:不去强制要求,由系统自行判断何时写入磁盘;
- 1:每次提交事务的时候都要将binlog写入磁盘;
- N:每 N 个事务,才会将binlog写入磁盘。
MySQL5.7 之前, sync_binlog 默认值为 0。在 MySQL5.7 之后, sync_binlog默认值为 1。
Server version: 8.0.27 MySQL Community Server - GPL
mysql> show variables like 'sync_binlog';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| sync_binlog | 1 |
+---------------+-------+
1 row in set (0.01 sec)
通常情况下,不建议将 sync_binlog 的值设置为 0 。如果对性能要求比较高或者出现磁盘 IO 瓶颈的话,可以适当将sync_binlog 的值调大,不过,这样会增加数据丢失的风险。
什么情况下会重新生成 binlog?
当遇到以下 3 种情况时,MySQL会重新生成一个新的日志文件,文件序号递增:
- MySQL服务器停止或重启;
- 使用
flush logs命令后; - binlog 文件大小超过
max_binlog_size变量的阈值后。