Skip to content

MySQL日志系统详解

MySQL日志系统是数据库管理、性能优化和故障排查的核心组件,记录了数据库运行过程中的各种关键事件和操作。本文将全面解析MySQL的主要日志类型、功能原理及实际应用场景。

MySQL日志系统概述

MySQL提供了多种日志类型,每种日志都有其特定的用途和记录机制。这些日志共同构成了MySQL数据库的"黑匣子",帮助DBA和开发人员理解数据库内部运行状态,诊断问题并优化性能。

根据功能和用途,MySQL日志主要分为以下几类:

  1. 错误日志(Error Log):记录服务器启动、运行和关闭过程中的错误、警告和关键事件
  2. 查询日志(General Query Log):记录所有客户端发送到服务器的查询语句
  3. 慢查询日志(Slow Query Log):记录执行时间超过阈值的查询语句
  4. 二进制日志(Binary Log):记录所有更改数据库数据的事件,用于数据恢复和复制
  5. 事务日志(Transaction Log)
    • 重做日志(Redo Log):保证事务的持久性
    • 回滚日志(Undo Log):保证事务的原子性
  6. 中继日志(Relay Log):主从复制过程中从服务器使用的日志

错误日志(Error Log)

核心功能与特点

错误日志是MySQL中最重要的日志之一,记录了服务器启动、运行和关闭过程中的错误、警告和关键事件信息。它是诊断MySQL问题的首要工具,具有以下核心功能:

  • 故障诊断:记录服务器启动/关闭问题和运行时错误
  • 安全审计:记录未授权的访问尝试
  • 系统监控:跟踪警告和关键事件
  • 复制问题:记录主从复制相关的错误
  • 操作审计:记录关键管理操作(如clean shutdown)

错误日志默认开启且无法被禁止,通常位于MySQL数据目录下,文件名为hostname.err(Linux)或mysqld.log(mac)。

配置与管理

错误日志的主要配置参数:

sql
[mysqld]
log_error = /var/log/mysql/mysql-error.log  # 错误日志文件路径
log_error_verbosity = 3  # 日志详细程度:1=ERROR, 2=ERROR+WARNING, 3=ERROR+WARNING+INFORMATION
log_error_services = log_filter_internal; log_sink_internal  # MySQL 8.0+日志服务组件
log_timestamps = SYSTEM  # 控制日志显示时间,建议设置为系统时区

查看当前错误日志位置:

sql
SHOW VARIABLES LIKE 'log_error';

日志内容分析

错误日志内容格式通常为:

bash
时间戳 [日志级别] [错误代码] [子系统] 错误信息

示例:

ini
2025-04-20T14:23:45.123456Z 0 [ERROR] [MY-010123] [Server] Access denied for user 'root'@'localhost'
2025-04-20T14:23:46.234567Z 0 [Warning] [MY-010000] [Server] InnoDB: Cannot open table test/corrupted_table
2025-04-20T14:23:47.345678Z 0 [Note] [MY-010000] [Server] Server shutdown initiated

常见错误代码及解决方案:

错误代码说明解决方案
MY-010123访问被拒绝检查用户权限
MY-012345表损坏使用REPAIR TABLE修复
MY-002233连接数耗尽增加max_connections参数
MY-003344磁盘空间不足清理磁盘空间或扩容存储

生产环境建议

  1. 将错误日志放在独立分区,避免影响系统运行
  2. 设置严格的访问权限,仅限管理员访问
  3. 对关键错误设置告警监控
  4. 建议每日检查错误日志
  5. 使用logrotate等工具进行日志轮转管理

查询日志(General Query Log)

功能与特点

查询日志记录了所有客户端发送到服务器的查询语句,无论这些查询是否成功执行。它提供了非常详细的查询记录,包括:

  • 所有发送到MySQL服务器的SQL命令(成功和错误的)
  • MySQL启动、关闭、更新等信息
  • 客户端连接/断开信息

由于查询日志记录非常详细,日志量通常很大,默认情况下是关闭的。一般只在排查特定问题时临时开启。

配置与管理

查询日志相关配置参数:

ini
[mysqld]
general_log = 1  # 1表示开启,0表示关闭
general_log_file = /var/log/mysql/mysql-query.log  # 日志文件路径
log_output = FILE  # 日志输出目标:FILE(文件)、TABLE(表)、NONE(不记录)

动态开启查询日志:

sql
SET GLOBAL general_log = 'ON';
SET GLOBAL log_output = 'FILE';

日志内容示例

查询日志内容格式通常为:

bash
时间 连接ID 命令类型 查询语句

示例:

ini
180421 20:04:41 13 Query select user,host from mysql.user
180421 20:06:06 13 Query show variables like "%error%"
180421 20:07:28 13 Query insert into ttt values(233)
180421 20:11:47 13 Query create table tt(id int)
180421 20:12:29 13 Query set @a:=3

使用建议

  1. 生产环境不建议长期开启查询日志,会显著影响性能
  2. 仅在需要排查特定问题时临时开启
  3. 确保日志文件所在分区有足够空间
  4. 使用完毕后及时关闭查询日志

慢查询日志(Slow Query Log)

核心功能与特点

慢查询日志记录执行时间超过指定阈值的SQL语句,是数据库性能优化的核心工具之一。它的主要功能包括:

  • 性能问题定位:识别执行效率低下的SQL语句
  • SQL优化依据:提供真实的SQL执行性能数据
  • 系统监控:监控数据库长期性能变化趋势
  • 索引优化:发现缺失或无效的索引使用情况

慢查询日志默认关闭,需要手动配置开启。记录时机是在SQL执行完成后(与general log不同)。

配置与管理

慢查询日志相关配置参数:

ini
[mysqld]
slow_query_log = 1  # 1表示开启,0表示关闭
slow_query_log_file = /var/log/mysql/slow-query.log  # 慢查询日志文件路径
long_query_time = 2  # 慢查询阈值,单位秒(支持微秒级)
log_queries_not_using_indexes = 0  # 是否记录未使用索引的查询,0=关闭
log_slow_admin_statements = 0  # 是否记录慢管理语句(如ALTER TABLE)
min_examined_row_limit = 0  # 查询扫描行数小于此值不记录

动态配置慢查询日志:

sql
-- 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
-- 设置慢查询阈值(单位:秒)
SET GLOBAL long_query_time = 2;
-- 设置慢查询日志文件路径
SET GLOBAL slow_query_log_file = '/var/log/mysql/slow-query.log';

日志内容分析

慢查询日志内容通常包括:

  1. 执行时间
  2. 用户和连接信息
  3. 查询执行时间、锁等待时间、返回行数、扫描行数
  4. 具体的SQL语句

示例:

sql
# Time: 170329 9:55:58
# User@Host: root[root] @ localhost [] Id: 1
# Query_time: 10.000847 Lock_time: 0.000000 Rows_sent: 1 Rows_examined: 0
use test;
select sleep(10);

分析与优化工具

mysqldumpslow - MySQL慢查询日志分析工具

命令简介

mysqldumpslow是MySQL自带的慢查询日志分析工具,用于解析和汇总MySQL慢查询日志文件。它能帮助数据库管理员快速识别执行缓慢的SQL语句,找出性能瓶颈,是MySQL性能调优的重要工具。

语法格式
bash
mysqldumpslow [options] [log_file ...]
选项
选项描述示例
-s ORDER排序方式(t:时间, l:锁定时间, r:返回记录, c:计数)mysqldumpslow -s t
-r反转排序结果mysqldumpslow -s t -r
-t N只显示前N条记录mysqldumpslow -t 10
-a不将数字替换为N,字符串替换为Smysqldumpslow -a
-g PATTERN只显示匹配模式的查询mysqldumpslow -g "SELECT"
-l不将锁定时间从总时间中减去mysqldumpslow -l
-v显示版本信息mysqldumpslow -v
-d调试模式mysqldumpslow -d
经典案例
bash
# 按查询时间排序,显示最慢的10个查询
mysqldumpslow -s t -t 10 /var/log/mysql/mysql-slow.log

# 按出现次数排序,显示最常见的慢查询
mysqldumpslow -s c -t 5 /var/log/mysql/mysql-slow.log

# 查找特定模式的慢查询(如包含JOIN的查询)
mysqldumpslow -g "JOIN" /var/log/mysql/mysql-slow.log

# 分析多个慢查询日志文件
mysqldumpslow /var/log/mysql/mysql-slow.log /var/log/mysql/mysql-slow.log.1

# 显示完整的SQL语句(不替换数字和字符串)
mysqldumpslow -a -s t -t 3 /var/log/mysql/mysql-slow.log
注意事项
  1. 使用前需确保MySQL慢查询日志已开启(设置slow_query_log=1
  2. 默认情况下,数字和字符串会被替换为N和S,使用-a选项可显示完整信息
  3. 分析结果中的"Count"表示该模式查询出现的次数,"Time"表示总执行时间
  4. 对于大型慢查询日志,建议使用-t选项限制输出数量
  5. 该工具在MySQL 8.0中仍可用,但官方推荐使用Performance Schema进行慢查询分析
  6. 输出中的"Lock"时间表示等待表锁的时间,不是查询执行时间

pt-query-digest - Percona高级MySQL日志分析工具

命令简介

pt-query-digest是Percona Toolkit中的强大分析工具,用于深入分析MySQL查询日志(包括慢查询日志、general log和二进制日志)。相比mysqldumpslow,它提供更全面的分析报告,包括查询响应时间分布、执行频率、表扫描统计等高级指标,是专业DBA进行数据库性能优化的核心工具。

语法格式
bash
pt-query-digest [options] [log_file]
选项
选项描述示例
--filter对查询进行过滤pt-query-digest --filter '$event->{arg} =~ m/^SELECT/i' slow.log
--limit=N%限制输出百分比pt-query-digest --limit=95% slow.log
--order-by排序字段(默认: Query_time:sum)pt-query-digest --order-by Lock_time:max slow.log
--review将结果存入表pt-query-digest --review h=localhost,D=test,t=query_review slow.log
--history保存历史趋势数据pt-query-digest --history h=localhost slow.log
--since分析指定时间后的查询pt-query-digest --since="2023-01-01" slow.log
--until分析指定时间前的查询pt-query-digest --until="2023-12-31" slow.log
--group-by分组字段(fingerprint,distill等)pt-query-digest --group-by=distill slow.log
--report-format输出格式(header,report,query_report等)pt-query-digest --report-format=query_report slow.log
经典案例
bash
# 基本慢查询日志分析(生成详细报告)
pt-query-digest /var/log/mysql/mysql-slow.log

# 分析并保存结果到数据库
pt-query-digest --review h=localhost,D=percona,t=query_review \
               --history h=localhost,D=percona,t=query_history \
               --no-report --limit=0% \
               /var/log/mysql/mysql-slow.log

# 实时监控processlist
pt-query-digest --processlist h=localhost --interval 0.01

# 分析tcpdump抓取的MySQL流量
tcpdump -s 65535 -x -nn -q -tttt -i any -c 1000 port 3306 > mysql.tcp
pt-query-digest --type=tcpdump mysql.tcp

# 生成HTML格式报告
pt-query-digest --report-format=html slow.log > report.html
注意事项
  1. 需要Perl环境支持,建议通过Percona官方仓库安装
  2. 分析大型日志文件时可能消耗较多内存,可考虑使用--sample选项
  3. 报告中的"Query_time distribution"可直观显示查询时间分布
  4. --review功能需要预先创建目标表结构
  5. 对于8.0+版本,建议结合Performance Schema数据进行分析
  6. 输出报告中的关键指标包括:
    • Exec time: 查询执行时间统计
    • Lock time: 锁定时间统计
    • Rows sent/examined: 行处理统计
    • Query size: 查询大小分布

生产环境建议

  1. 合理设置long_query_time阈值(通常1-2秒)
  2. 不建议长期开启log_queries_not_using_indexes,会导致日志快速增长
  3. 定期分析慢查询日志并优化问题SQL
  4. 使用logrotate等工具进行日志轮转管理
  5. 对于频繁出现的慢查询,考虑添加适当索引或重写SQL

二进制日志(Binary Log)

核心功能与特点

二进制日志(binlog)是MySQL最重要的日志之一,记录所有更改数据库数据的DDL和DML语句(不包括SELECT、SHOW等查询语句)。它的主要用途包括:

  • 数据恢复:可以将以前提交的更改应用到数据库以恢复数据
  • 数据备份和复制:支持将数据从一个MySQL服务器复制到另一个服务器
  • 数据一致性检查:确保主从数据库数据一致

二进制日志是MySQL Server层的逻辑日志,与存储引擎无关,只在事务提交时才写入。

记录格式

二进制日志有三种记录格式:

格式类型特点适用场景
STATEMENT(SBR)记录SQL语句本身5.7之前默认,日志量小
ROW(RBR)记录行数据变更数据安全,5.7+默认
MIXED(MBR)混合模式平衡安全与效率

查看当前格式:

sql
SHOW VARIABLES LIKE 'binlog_format';

写入机制

事务执行过程中,日志先写到binlog cache,事务提交时才写入binlog文件:

  1. 事务执行过程中,先把日志写到binlog cache
  2. 事务提交时,再把binlog cache写到binlog文件中

相关参数:

  • binlog_cache_size:控制binlog cache大小
  • sync_binlog:控制binlog写入磁盘的策略

MySQL GTID模式

GTID(Global Transaction Identifiers)是MySQL 5.6版本引入的一种全局事务标识机制,它为每个事务分配一个全局唯一的标识符,极大地简化了主从复制的配置和管理。下面我将从多个角度详细介绍GTID模式。

GTID的基本概念与组成

  1. source_id:即服务器的server_uuid,是一个128位的全局唯一标识符,存储在MySQL数据目录下的auto.cnf文件中。格式如:3E11FA47-71CA-11E1-9E33-C80AA9429562
  2. transaction_id:是一个整数序列号,表示在该服务器上执行的事务顺序,从1开始递增。例如1-77表示从序列号1到77的事务完整的GTID示例:3E11FA47-71CA-11E1-9E33-C80AA9429562:23

GTID的工作原理

  1. 事务在主库执行并提交时,系统会为其分配一个GTID,并记录到binlog中
  2. binlog传输到从库并存入relay log后,从库读取GTID值设置gtid_next变量
  3. SQL线程从relay log获取GTID,对比从库binlog是否已有该GTID
  4. 如果已有记录,则忽略该事务;如果没有记录,则执行该事务并记录GTID到自身binlog
  5. 在解析过程中会判断是否有主键,如果没有就用二级索引,如果没有就用全表扫描

GTID模式的优势

  1. 简化主从切换:不再需要手动记录二进制日志文件和位置,使用MASTER_AUTO_POSITION=1即可自动定位复制点
  2. 提高复制可靠性:确保事务在主从复制中的顺序一致性,减少数据不一致风险
  3. 故障恢复便捷:可以快速确定事务最初提交的实例,便于故障定位和恢复
  4. 支持并行复制:基于GTID可以更好地实现并行复制,提高复制效率
  5. 自动跳过重复事务:如果事务已在从库执行过,GTID机制会自动跳过
  6. 简化备份恢复:备份和恢复可以基于GTID而非文件偏移量,操作更简单

GTID模式的配置

ini
[mysqld]
# 是否启用GTID模式(ON/OFF)
gtid_mode=ON
# 强制GTID一致性(ON/OFF)
enforce_gtid_consistency=ON

查看GTID

sql
SHOW GLOBAL VARIABLES LIKE 'gtid%';
SHOW GLOBAL VARIABLES LIKE 'server_uuid';

配置与管理

二进制日志配置参数

sql
[mysqld]
log_bin = mysql-bin  # 启用二进制日志并设置文件名前缀
binlog_format = ROW  # 二进制日志格式:STATEMENT/ROW/MIXED
max_binlog_size = 100M  # 单个二进制日志文件大小
expire_logs_days = 7  # 二进制日志保留天数
sync_binlog = 1  # 控制binlog写入磁盘的频率,1表示每次事务提交都写入

二进制日志操作命令

sql
-- 查看二进制日志启用情况
SHOW VARIABLES LIKE '%log_bin%';

-- 查看当前二进制日志文件列表
SHOW BINARY LOGS;
-- MySQL 5.7 查看主数据库服务器(Master)的二进制日志状态信息。
SHOW MASTER STATUS;
-- MySQL 8.0+ 查看主数据库服务器的二进制日志状态信息。
SHOW BINARY LOG STATUS;

-- 删除指定日志之前的二进制日志
PURGE BINARY LOGS TO 'mysql-bin.000010';

mysqlbinlog - MySQL二进制日志解析工具

mysqlbinlog是MySQL自带的二进制日志解析工具,用于查看和分析MySQL的二进制日志文件(binlog)。它可以将二进制日志文件转换为可读的SQL语句形式,便于数据库管理员进行数据恢复、审计或复制故障排查。

语法格式
bash
mysqlbinlog [options] log_file ...
选项
选项描述
--start-datetime=datetime从指定时间开始读取日志(格式:'2023-01-01 00:00:00')
--stop-datetime=datetime在指定时间停止读取日志(格式:'2023-01-01 00:00:00')
--start-position=N从指定位置开始读取日志
--stop-position=N在指定位置停止读取日志
-d db_name仅显示指定数据库的日志
--base64-output=value控制BASE64编码输出(可选:AUTO/NEVER/DECODE-ROWS)
-v显示更详细的信息,特别是行事件
--rewrite-db='from->to'重写数据库名
--result-file=name将输出重定向到指定文件
--include-gtids只包含指定GTID集合的事务
--exclude-gtids排除指定GTID集合的事务
--stop-position指定结束位置
--start-datetime按时间范围备份
--stop-datetime结束时间点
--raw输出原始二进制格式
--read-from-remote-server从远程服务器读取binlog
经典案例
bash
# 查看特定时间段的binlog内容
mysqlbinlog --start-datetime="2023-01-01 09:00:00" \
            --stop-datetime="2023-01-01 10:00:00" \
            mysql-bin.000001

# 将binlog转换为SQL文件用于恢复
mysqlbinlog --start-position=107 --stop-position=1000 \
            mysql-bin.000001 > recovery.sql

# 查看特定表的变更(需要配合grep)
mysqlbinlog -v mysql-bin.000001 | grep -A 10 "UPDATE \`users\`"

# 解码行事件并显示完整的SQL语句
mysqlbinlog --base64-output=DECODE-ROWS -v mysql-bin.000001

# 恢复到特定GTID之前
mysqlbinlog --include-gtids='3a59e21e-71ca-11e1-9e33-c80aa9429562:1-150' mysql-bin.000012 | mysql -u root -p

# 跳过特定GTID恢复
mysqlbinlog --exclude-gtids='3a59e21e-71ca-11e1-9e33-c80aa9429562:120-130' mysql-bin.000012 | mysql -u root -p
注意事项
  1. 使用mysqlbinlog需要具有读取二进制日志文件的权限
  2. 在生产环境中执行binlog中的SQL语句前,务必先进行测试
  3. 使用--start-position--stop-position可以更精确地定位事件
  4. 对于大型binlog文件,建议将输出重定向到文件而不是直接显示在终端
  5. 使用-v选项可以查看行事件的详细信息,但输出会变得非常详细
  6. 在MySQL 5.7+版本中,可能需要添加--read-from-remote-server选项来读取远程服务器的binlog

与Redo Log的关系

MySQL使用两阶段提交机制确保binlog和redo log的一致性:

  1. 生成redo log放入Redo Log Buffer,状态为prepare
  2. 事务提交后,binlog写入成功
  3. 将Redo Log Buffer中的redo log刷新到磁盘,状态改为commit

这种机制确保了即使数据库崩溃,也能通过比较binlog和redo log状态进行正确恢复。

事务日志(Transaction Log)

重做日志(Redo Log)

功能与特点

重做日志(redo log)是InnoDB存储引擎特有的物理日志,记录事务提交时数据页的物理修改,主要功能是保证事务的持久性和数据库的崩溃恢复能力。

关键特点:

  • 物理日志,记录"在某个数据页上做了什么修改"
  • 事务过程中不断写入
  • 大小固定,采用循环写入方式,前面的内容会被覆盖

写入机制

  1. 客户端提交数据修改时,先去Buffer Pool获取数据
  2. 生成redo log放入Redo Log Buffer,记录数据页的物理变化(状态为prepare)
  3. 事务提交后,将Redo Log Buffer中的redo log刷新到磁盘(状态改为commit)

WAL机制

InnoDB采用Write-Ahead Logging(WAL)机制:

  • 先写日志,再写磁盘
  • 即使Buffer Pool中的脏页刷新到磁盘时出错,也可以通过redo log重新刷新

"脏页"是指内存数据页与磁盘数据页内容不一致的内存页。

回滚日志(Undo Log)

功能与特点

回滚日志(undo log)用于记录数据被修改前的信息,主要功能是:

  • 异常回滚,保证事务的原子性
  • 实现MVCC(多版本并发控制)机制

关键特点:

  • 当delete一条数据时,会插入一条对应的insert记录到undo log
  • 当update一条记录时,会插入一条相反的update记录到undo log
  • 执行回滚时,可以读取undo log中的记录进行逆向操作

版本链

不同事务对同一条记录的修改会形成undo log版本链:

  • 链表头部是最新的旧记录
  • 链表尾部是最早的旧记录
  • MVCC的ReadView决定事务能看到版本链上的哪条历史数据

配置参数

[mysqld]
innodb_undo_directory = /mysqldata/  # undo日志目录
innodb_undo_tablespaces = 4  # undo表空间数量
innodb_undo_log_truncate = ON  # 启用undo日志截断
innodb_max_undo_log_size = 1G  # undo表空间大小阈值

中继日志(Relay Log)

功能与特点

中继日志(relay log)是主从复制过程中从服务器(I/O线程)使用的日志,记录了从主服务器接收的二进制日志事件。主要特点包括:

  • 格式与二进制日志基本相同
  • 由从服务器的I/O线程从主服务器读取并写入
  • 从服务器的SQL线程读取中继日志并执行其中的事件

主从复制流程

  1. 主服务器将数据更改记录到二进制日志(binlog)
  2. 从服务器I/O线程从主服务器读取binlog事件并写入中继日志
  3. 从服务器SQL线程读取中继日志并执行其中的事件

配置参数

sql
[mysqld]
relay_log = /var/lib/mysql/mysql-relay-bin  # 中继日志文件路径
relay_log_index = /var/lib/mysql/mysql-relay-bin.index  # 中继日志索引文件
relay_log_info_file = relay-log.info  # 中继日志信息文件
relay_log_purge = ON  # 自动清理已完成复制的日志

日志管理最佳实践

日志文件过大问题处理

原因:长时间运行或大量数据操作导致日志文件迅速增长

解决方案

  • 定期清理和归档日志文件
  • 调整日志滚动策略(按大小或时间滚动)
  • 对于二进制日志,设置expire_logs_days自动清理

日志文件权限问题

原因:MySQL用户对日志文件的读写权限不足

解决方案

sql
sudo chown -R mysql:mysql /var/lib/mysql
sudo chmod -R 755 /var/lib/mysql

日志记录过于详细

原因:查询日志或慢查询日志记录了过多信息,导致性能下降

解决方案

  • 调整日志级别或关闭不必要的日志记录
  • 使用更高效的日志记录工具或方法
  • 仅在生产环境问题排查时开启详细日志

日志轮转配置示例

使用logrotate管理MySQL错误日志:

sql
/var/log/mysql/mysql-error.log {
    daily
    rotate 30
    missingok
    compress
    delaycompress
    notifempty
    create 640 mysql mysql
    postrotate
        mysqladmin flush-logs
    endscript
}

生产环境建议

  1. 根据业务需求合理配置各类日志级别和保留策略
  2. 为日志文件分配独立磁盘分区,避免影响数据库性能
  3. 建立日志监控告警机制,及时发现潜在问题
  4. 定期审查和分析日志,持续优化数据库性能
  5. 重要日志(如binlog)建议异地备份

常见问题解决方案

MySQL启动失败

错误信息示例

sql
[ERROR] Can't start server: Bind on TCP/IP port: Address already in use

解决方案

sql
# 查找占用3306端口的进程
sudo lsof -i :3306
# 终止占用进程(假设进程ID是1234)
sudo kill -9 1234
# 重新启动MySQL服务
sudo systemctl restart mysql

数据目录损坏

错误信息示例

sql
InnoDB: Database was not shut down normally!

解决方案

sql
sudo mysqld --initialize --user=mysql --console

表损坏

错误信息示例

sql
[ERROR] [MY-012345] [Server] InnoDB: Cannot open table test/corrupted_table

解决方案

sql
REPAIR TABLE corrupted_table;

连接数耗尽

错误信息示例

sql
[ERROR] [MY-002233] [Server] Too many connections

解决方案

sql
[mysqld]
max_connections = 500  # 增加最大连接数