news 2026/10/22 15:58:34

MySQL日志查看实战:错误日志、慢查询与binlog排查指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL日志查看实战:错误日志、慢查询与binlog排查指南

排查MySQL问题的时候,第一件事永远是“MySQL查看日志”。这句话听起来像是句废话,但真正落实到操作层面,坑比想象中多:日志文件不止一个、位置五花八门、有的默认关闭、有的写多了还反过来拖垮数据库性能。很多新手遇到故障,第一反应是复制报错去网上搜,结果白折腾几个小时,不如老老实实把日志打开看一眼来得快。这篇文章我把日常和日志打交道的经验整理一遍,涵盖错误日志、慢查询日志、通用查询日志、二进制日志,以及排查中最容易踩的坑,适合刚接手MySQL维护的开发者,也适合想把手头数据库问题处理利索的人。

1. 日志分类:MySQL到底藏了哪些记录在案的东西

1.1 五类核心日志,各自管什么

MySQL的日志不是简单的一个文件,而是一整套“留痕”体系。按功能划分,日常打交道最多的有五类:错误日志(error log)、慢查询日志(slow query log)、通用查询日志(general query log)、二进制日志(binary log)和中继日志(relay log)。很多人会把它们混为一谈,比如出了问题就只知道翻错误日志,SQL慢了也不知道去开慢查询日志,数据被误删了更是想不到binlog里其实还有底账。搞清楚每类日志管什么,是“查看日志”的第一步。

  • 错误日志:记录MySQL服务启动、运行、关闭过程中的错误和告警,比如启动失败、连接异常、InnoDB损坏、主从报错。这是排查故障时的第一现场。
  • 慢查询日志:记录执行时间超过阈值的SQL语句,是性能优化最重要的依据。数据库变慢、接口超时,基本都是从这里找到元凶。
  • 通用查询日志:记录MySQL收到的每一条客户端请求,包括连接、断开、SQL语句。相当于全量审计日志,但生产环境长期开着很伤性能。
  • 二进制日志:记录所有导致数据变更的事件,包括增删改、建表、权限变更。主从复制靠它,数据误删恢复也靠它。
  • 中继日志:主从架构里从库专用的日志,主库的binlog传到从库后先写上中继日志,再由从库的SQL线程回放。

顺带提一句,MySQL还有InnoDB引擎层的redo log和undo log,这两类属于内部日志,通常不需要直接查看文件。想要了解它们的工作状态,一般用 SHOW ENGINE INNODB STATUS 就足够了,如果去数据目录里翻ib_logfile文件,那属于给自己找麻烦。

1.2 日志开关和落盘位置怎么查

在我接触过的环境里,日志文件位置几乎每个实例都不一样,所以别背路径,背命令。最靠谱的方式是用以下两个变量直接问MySQL,它自己最清楚自己把日志写到哪了:

SHOW VARIABLES LIKE 'log_error'; SHOW VARIABLES LIKE 'slow_query_log_file'; SHOW VARIABLES LIKE 'general_log_file'; SHOW VARIABLES LIKE 'log_bin';

如果log_error没有显式配置过,默认通常在数据目录下,文件名一般是主机名点err这种格式。比如实例的主机名是db-server,错误日志可能就是/var/lib/mysql/db-server.err。如果系统用apt或yum安装的MySQL,很多发行版会预先把日志路径改到/var/log/mysql/目录下,这时候上面这条SQL查出来的路径就是最权威的答案。

我个人的习惯是:接手任何一台MySQL服务器,先执行一遍上面四条SQL,把结果记到自己的服务器清单里。别小看这个习惯,真到凌晨三点数据库起不来的时候,你不可能还有心思去猜日志文件在哪。另外还要注意看log_output参数,它决定日志是写到文件(FILE)还是写到系统表(TABLE)。默认是FILE,如果某些环境改成了TABLE,那错误日志、慢查询日志、通用日志都可以直接用SQL查mysql.slow_log、mysql.general_log表来定位,这对后续分析很有帮助。

2. 错误日志:故障排查的第一现场

2.1 错误日志怎么定位、怎么持续跟踪

错误日志是MySQL自己写的“日记”,记录的是它从启动到运行过程中遇到的各种异常。启动失败、改配置后起不来、端口被占用、数据文件权限不对、磁盘空间满了,这些信息基本都只会出现在错误日志里。很多新手遇到数据库启动失败,跑去查系统日志,绕了一大圈,其实MySQL自己已经把原因写清楚了。

定位错误日志路径的方法上面已经说过,最常用的是SHOW VARIABLES LIKE 'log_error'。找到文件之后,不要一上来就cat整个文件。日志文件动辄几百MB,直接cat会把终端卡死,还什么都看不到。正确姿势是先看末尾几十行:

tail -n 100 /var/log/mysql/error.log

如果问题还在持续发生,想实时跟踪新写入的错误内容,用tail -f或者less打开后按大写F键进入follow模式。我通常配合grep一起用,只过滤出错误级别以上的内容:

grep -E "ERROR|WARNING" /var/log/mysql/error.log | tail -n 50

MySQL 8.0之后还有一个log_error_verbosity参数,取值范围是1到3,数字越大输出越详细。平时设置为2基本够用;如果某个问题需要更多上下文,临时调到3再复现一次,往往能看到更完整的堆栈信息。但注意调太高的时候日志量会明显增加,排查完记得改回来。

2.2 高频错误日志内容解读

下面列几个我在实战里反复看到的错误日志片段,以及对应的排查思路,帮你对号入座。

[ERROR] Can't start server: Bind on TCP/IP port: Permission denied

这个报错的意思是MySQL想监听3306端口,但操作系统拒绝了。最常见的原因有两个:一是端口被其他进程占用了,执行netstat -tlnp | grep 3306看一下是谁在用;二是MySQL在非root用户下启动,而代码里配置了监听1024以下的端口(虽然3306不在此列,但如果改了端口就要注意)。处理方式是把占用进程停掉,或者修改port参数换一个端口。

[ERROR] InnoDB: Unable to lock ./ibdata1, error: 11

看到Unable to lock基本可以断定是数据目录被另一个mysqld进程锁住了。简单说就是MySQL已经在跑,或者上次没干净退出留下了一个ibdata1锁。解决办法是先确认没有重复进程,如果确认没有,看数据目录下是否有*.pid等锁文件残留,清理后再启动。

[ERROR] Table './mydb/user' is marked as crashed and should be repaired

这是MyISAM表损坏的经典报错。虽然现在生产环境绝大多数用InnoDB,但老项目里偶尔还有残留的MyISAM表。修复方式是对表做CHECK TABLE和REPAIR TABLE:

CHECK TABLE mydb.user; REPAIR TABLE mydb.user;

如果是InnoDB表出现类似InnoDB: Database page corruption的报错,那问题要严重得多,通常是磁盘坏道或异常断电导致。这时候最稳妥的手段是马上从备份恢复,不建议自己随便动数据文件。如果确实没有备份,可以尝试用innodb_force_recovery参数把实例拉起来再做逻辑导出,但这是死马当活马医的手段,导出的数据可能不完整,只能尽力而为。

[ERROR] Access denied for user 'root'@'localhost' (using password: YES)

有时候这条会出现在错误日志里,虽然它本质上属于认证失败,但连续出现说明有程序在反复用错误密码连接数据库。这种情况要重点排查是否有应用配置了旧密码,或者有人在暴力尝试登录。生产环境建议把log_error_verbosity保持在一个合理的级别,至少能让你发现这种异常尝试。

3. 慢查询日志:性能遇瓶颈时最先开的东西

3.1 慢查询日志的核心参数和开启方式

数据库响应变慢、接口超时、CPU使用率飙高,这种时候我最先做的事情就是开启慢查询日志。它的核心原理很简单:MySQL在执行每条SQL时会计时,超过你设定阈值(long_query_time)的语句,连同执行时间、锁等待时间、返回行数、扫描行数一起写进日志。

先看当前状态:

SHOW VARIABLES LIKE 'slow_query_log'; SHOW VARIABLES LIKE 'long_query_time'; SHOW VARIABLES LIKE 'log_queries_not_using_indexes';

默认情况下slow_query_log可能是OFF,long_query_time默认10秒。判断要不要改阈值,得看业务体量。我自己的经验是:一个线上OLTP系统,如果单条SQL超过1秒就已经值得警惕;很多开发环境的联调接口,几百毫秒的慢SQL就会拖垮整体体验。所以一般建议生产环境把long_query_time设置成1秒,如果系统压力大、性能要求高,甚至可以设置到0.5秒。

动态开启,不需要重启:

SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; SET GLOBAL log_queries_not_using_indexes = 'ON';

这里有几个容易踩的细节。第一,SET GLOBAL long_query_time只对新建立的会话生效,已经连着的客户端不受影响,所以测试时如果觉得“我明明设置了1秒,为什么还是没记录”,先重连一下。第二,log_queries_not_using_indexes打开之后,所有没走索引的查询也会被记进慢日志,哪怕它执行只要几毫秒。这在排查全表扫描时非常有用,但也要注意日志量可能暴增,因为一个设计不好的系统里,不走索引的小查询可能一抓一大把。第三,动态设置的参数重启后会恢复默认值,如果确认需要长期开启,记得把配置写进/etc/my.cnf的[mysqld]段。

3.2 一个定位慢SQL的完整过程

我举一个非常有代表性的案例,这种场景你在生产环境大概率会碰到。某天业务方反馈一个报表查询越跑越慢,从原来的两三秒变成了十几秒,而且每天早上跑报表的时候数据库负载明显飙高。

我的处理过程是这样的。先确认慢日志已开启,然后等第二天问题复现后,查看慢日志末尾:

tail -n 200 /var/lib/mysql/*-slow.log

找到那条报表SQL,记录它执行时间、扫描行数、返回行数。日志里会有一行类似Rows_sent: 5000 Rows_examined: 4000000的信息,看到延迟秒数和扫描行数,基本可以断定存在大范围扫描。接着用EXPLAIN分析执行计划:

EXPLAIN SELECT ...

通常问题一目了然:某个大表关联时没走索引,type列显示ALL,Extra列出现Using temporary和Using filesort。前者代表产生了临时表,后者代表在磁盘上做了排序,这两个都是性能杀手。解决方案也很清晰——给关联字段和排序字段建立联合索引。加完索引再执行一遍,查询时间从十秒级降到毫秒级。

整个过程里慢查询日志扮演的就是“雷达”角色。没有它,你根本不知道是哪一条SQL在作妖;有了它,你连SQL的执行细节、时长、扫描行数都看得清清楚楚,后续优化就有了依据。这也是为什么我强烈建议哪怕系统暂时没有性能问题,也把慢查询日志开着,记录阈值设得稍微宽松一点(比如2秒),成本不高,但哪天出事了你就有据可查。

3.3 慢日志分析的辅助工具

慢日志记录多了以后,人眼逐条看是不现实的。MySQL自带了一个工具叫mysqldumpslow,我的日常工作流里用得很多。

mysqldumpslow -s t -t 10 /var/lib/mysql/*-slow.log

这条命令的含义是按执行时间排序,取出前10条最慢的SQL。它还会自动把参数值替换成N,把结构相同的SQL聚合到一起,这个功能非常有用。比如SELECT * FROM user WHERE id = 12345和SELECT * FROM user WHERE id = 67890会被合并成一条统计,让你一眼看出哪种SQL模式是真正的高频慢查询。

如果系统里装了Percona Toolkit,那么pt-query-digest是更专业的分析工具。它能按月、按天汇总SQL,输出每个模式的响应时间占比、执行次数、平均耗时,还会给出一个比较详细的报表。我在处理历史遗留系统的时候,经常用pt-query-digest对一周的慢日志做整体分析,定位哪些SQL模式占用数据库资源最多,再逐条做优化。

4. 通用查询日志和二进制日志:审计与恢复的两把钥匙

4.1 通用查询日志:什么时候开、怎么开最安全

通用查询日志(general log)记录的是MySQL收到的每一条请求,不管是连接、断开还是执行SQL,全部记录。它的作用更像“黑匣子”,适合用来回答“这个SQL到底是谁发出来的”“应用连上数据库之后执行了什么诡异操作”这类问题。

举个例子,有一次线上出现数据被莫名修改的情况,代码层排查了很久都没找到来源。后来我在低峰期临时打开通用日志,等了几个小时,果然发现一个已经下线很久的后台任务还在用旧账号定时执行更新操作。这种问题靠应用日志根本发现不了,只有通用日志能全量留痕。

开启方式:

SET GLOBAL general_log = 'ON'; SET GLOBAL general_log_file = '/var/log/mysql/general.log';

注意,通用日志是所有类型日志里写盘量最大、性能影响最直接的。高并发服务如果长期开着general_log,不只是日志文件疯狂膨胀,MySQL的整体吞吐量也会明显下降,因为每次SQL执行都要多一次日志写入。所以我的建议很简单概括为“临时开、用完关”。排查完毕后马上关掉:

SET GLOBAL general_log = 'OFF';

另外还有一个技巧。如果担心文件日志影响磁盘IO,可以临时把log_output切换成TABLE,这样日志会写到mysql.general_log表里,你可以用SQL查询和分析,用完再关闭并清空这张表。但注意log_output是全局参数,切换时会影响错误日志和慢查询日志的输出方式,操作前最好确认当前状态,并且在低峰期执行。

4.2 二进制日志:用mysqlbinlog做数据恢复

二进制日志(binlog)是MySQL里含金量最高的一类日志,因为它记录了所有导致数据变更的事件。主从复制靠它,数据误删恢复也靠它。我先说几个必须掌握的状态查询命令:

SHOW BINARY LOGS; SHOW MASTER STATUS;

SHOW BINARY LOGS列出当前所有binlog文件,SHOW MASTER STATUS显示当前正在写哪个binlog、写到什么位置。binlog默认在datadir下,文件名类似mysql-bin.000001。是否开启由log_bin参数决定,MySQL 8.0下如果要开启,还必须配置server_id。

binlog是二进制文件,不能直接cat,要用mysqlbinlog工具解析,最基础的用法:

mysqlbinlog --no-defaults /var/lib/mysql/mysql-bin.000012

它会输出一系列伪SQL和注释信息。但如果binlog_format是ROW格式,默认看到的是base64编码的事件,想要还原成可读SQL,要加--base64-output=DECODE-ROWS -vv:

mysqlbinlog --no-defaults --base64-output=DECODE-ROWS -vv /var/lib/mysql/mysql-bin.000012

我实际处理过一次误删数据恢复。某天运维同学在生产库上执行了一条DELETE语句,把一张表里的多行数据删掉了。因为没带备份条件,直接全量删除,幸好binlog是ROW格式。当时的恢复思路是先用时间范围定位误操作发生的binlog位置:

mysqlbinlog --no-defaults --start-datetime='2024-03-20 10:00:00' --stop-datetime='2024-03-20 10:10:00' /var/lib/mysql/mysql-bin.000020

找到对应的DELETE事件后,用--start-position和--stop-position精确截取出误操作前后的日志,然后在测试库上把日志回放,把被删前的数据提取出来,再插回生产表。这是典型的“不完全恢复”场景。如果数据被删得更多,可以用mysqlbinlog做基于时间点的增量恢复:mysqlbinlog --start-datetime=... mysql-bin.000020 | mysql -uroot -p,配合之前的全量备份,把数据恢复到误操作前一刻。

这个场景里最关键的一点是:binlog_format必须为ROW。如果是STATEMENT格式,日志里只记录原始SQL语句,遇到不带WHERE的DELETE或UPDATE,恢复难度就大很多。所以在MySQL 8.0里我一般直接使用默认的ROW格式,既能保证数据恢复精度,也能保证主从复制的一致性。

5. 中继日志与日志文件日常管理

5.1 主从复制里的中继日志怎么看

中继日志(relay log)只在从库上存在,是主从复制链路里的中间产物。主库的binlog传到从库后,从库的IO线程先把它写成relay log,再让SQL线程读取并回放。大多数人平时不会直接碰relay log,但主从复制出问题的时候,它就是关键的排查点。

先通过主从状态看当前relay log情况:

SHOW SLAVE STATUS\G

重点关注几个字段:Relay_Log_File表示当前正在读取的中继日志文件,Relay_Log_Pos表示读取到的偏移量,Relay_Master_Log_File表示这个中继日志对应主库的哪个binlog。如果Slave_IO_Running和Slave_SQL_Running任何一个显示为No,说明复制已经断开了。这个时候错误日志里通常会有具体原因,比如日志文件找不到、回放SQL报错、主键冲突等。SQL线程报错时,可以根据Last_Errno和Last_Error定位具体是哪条语句出了问题。

中继日志文件损坏的情况虽然不常见,但真遇到了也不要慌。如果只是单纯的relay log文件异常,但主库binlog还完好,可以考虑用RESET SLAVE ALL清除复制关系后重新配置。不过操作前一定要确认当前主从关系和binlog位置,避免造成数据不一致。如果有条件,重新搭建从库往往更稳妥。

5.2 日志膨胀问题怎么处理

日志文件如果放任不管,迟早把磁盘写满。数据库磁盘写满的后果比应用磁盘写满严重得多:MySQL可能直接拒绝写入,甚至整个实例宕机。所以日志的日常管理机制必须提前设计好。

针对不同类型的日志,处理方式不一样。

  • 通用日志、慢日志、错误日志:可以定期用mv把当前日志改名,再执行mysqladmin flush-logs让MySQL生成新日志文件。改名的旧日志可以压缩归档,保留一定周期后清理。
  • binlog:千万不能直接用rm命令删。binlog的删除要交给MySQL自己管理,要么通过binlog_expire_logs_seconds参数自动过期,要么手动执行PURGE BINARY LOGS。举个例子,保留7天日志可以这样配:
binlog_expire_logs_seconds=604800

手动清理到指定文件为止:

PURGE BINARY LOGS TO 'mysql-bin.000020';

按时间清理:

PURGE BINARY LOGS BEFORE NOW() - INTERVAL 7 DAY;

还要特别提醒一句:ib_logfile*是InnoDB的redo log,不是普通日志文件,如果按照上面的思路去mv或者删,轻则实例起不来,重则直接数据损坏。这类文件不要做任何手工处理,滚动管理交给InnoDB引擎自己完成。

6. 常见故障与日志排查对照表

6.1 不同故障该翻哪类日志

我在实际运维中经常被问到“数据库出问题了,我该看哪个日志”。下面这个表我整理了很久,基本覆盖了日常工作里最常见的几类故障,可以先看着这张表快速定位方向,再深入具体日志去做细节排查。

故障现象优先查看的日志关键排查点
MySQL启动失败、服务崩溃错误日志bind失败、权限问题、innodb恢复报错
SQL执行慢、接口超时慢查询日志long_query_time阈值、扫描行数、是否走索引
数据被误改、误删二进制日志binlog_format是否为ROW、定位误操作时间点和position
应用发出的SQL和预期不符通用查询日志全量记录客户端发出的每一条SQL
主从复制延迟或中断错误日志+中继日志IO线程/SQL线程状态、relay log是否损坏
连接数打满、无法新建连接错误日志+通用日志Access denied、连接风暴、认证失败记录

这张表能帮你省掉不少瞎翻日志的时间。我自己遇到问题的第一反应不是去翻代码,而是先按这个思路确认“故障属于哪一类,日志去哪看”,方向对了,排查效率至少翻一倍。

6.2 几个提升效率的实操习惯

最后再分享几个我这些年打磨出来的日志操作习惯,算不上高深,但很实用。

第一个习惯是“日志文件必须旋转”。尤其错误日志和慢日志,长期不切分的情况下,一个文件跑到几个GB很正常,你排查的时候随便一条查询就要等半天。我一般配合cron脚本,每天夜里把错误日志和慢日志切换一次,旧日志压缩保留30天。切换动作很简单:mv旧文件后执行mysqladmin flush-logs,注意如果用了logrotate这类工具,记得告诉它不要直接kill掉mysqld进程,要让MySQL自己刷新文件句柄。

第二个习惯是“开发环境也开慢日志”。很多人只在生产环境开慢日志,开发阶段一关到底。我个人的看法是,开发环境反而应该开,而且long_query_time可以设得更苛刻一些,比如0.5秒甚至0.2秒。开发阶段SQL写烂了,执行计划全表扫描,日志里立刻现形,改起来成本极低。等代码上了生产再发现,那就要跟业务方解释半天为什么接口又慢了。

第三个习惯是“分析慢日志前先做一轮聚合”。太大的慢日志文件里可能几千条SQL,直接打开找重点非常低效。先用mysqldumpslow -s t -t 20拉出Top 20,再针对这些SQL逐条用EXPLAIN优化,一轮下来性能问题能解决大半。慢日志的价值不在于记录,而在于帮你找出那些高频、高耗时的SQL模式,优化它们才是正经事。

第四个习惯也是我自己最受益的一条:每次排查完问题,把日志关键报错、当时的参数配置和最终解决方案记到自己的排障文档里。数据库领域很多问题是重复性的,日志报错也就那些套路,整理过一遍之后,下次再遇到同样的问题,你都不用查日志,扫一眼报错就能直接定位。这个习惯坚持一两年,你会发现自己对MySQL的熟悉程度会提升一大截,遇到故障也能从被别人带着走变成带着别人走。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/22 15:56:17

SolidWorks对称配合实战:解决左右镜像件装配难题

做装配体设计的人,应该都遇到过这种尴尬:左侧板十个孔、右侧板十个孔,尺寸完全一样,方向完全相反,装的时候却总是差那么一口气。以前我处理这种左右镜像件,要么在零件图里重新建模,要么在装配体…

作者头像 李华
网站建设 2026/10/22 15:56:14

用MCP协议打造AI安全助手:从威胁情报到日志分析的实战指南

1. 项目概述:为什么安全人员需要自己的AI助手做了这么多年安全运营,我最大的感受是:我们每天干的活里,有一大半是重复且模式化的。IP威胁查询、CVE漏洞详情翻阅、日志格式整理、告警初筛、恶意文件Hash查验——这些工作说难不难&a…

作者头像 李华
网站建设 2026/10/22 15:55:46

九月开源项目盘点:20个自动化与本地优先工具的技术选型指南

1. 九月份开源圈为什么值得单独盘一次九月份在开源社区里一直是个特殊的月份。北半球的开发者结束休假回到工位,各大基金会开始为年底的版本冻结做冲刺,很多团队也会赶在这个时间点把憋了大半年的项目开源出来。我翻了一圈九月份冒头的项目,发…

作者头像 李华
网站建设 2026/10/22 15:54:55

多Agent架构面试怎么答?四种经典架构与选型指南

1. 面试官问"多 Agent 架构"时,到底在考察什么面到多 Agent 系统这道题,很多人第一反应是背概念:什么"多个智能体协作完成任务"、什么"分工明确各司其职"。但真坐到面试桌对面,面试官抛出一句"…

作者头像 李华
网站建设 2026/10/22 15:53:41

大模型驱动的UI视觉回归:基于千问3.5-35B-A3B-FP8的实践

自从把大模型接进自动化测试流程,我明显感觉到UI视觉回归这个老顽疾终于有了被根治的苗头。过去跑一轮视觉回归,最怕的不是截图失败,而是半夜收到几十条告警,点开一看全是无用功——按钮颜色浅了一个色号被标红、接口返回的随机用…

作者头像 李华
网站建设 2026/10/22 15:53:04

Agent治理:为什么管好意图流比优化token更重要

1. 这句话到底在说什么:当“管Agent”变成一场认知错位“Agent真正难管的,不是token”——这句话最近在技术圈里反复刷屏,但很多人点开文章后反而更迷糊了。它既不像“模型越大会不会越蠢”那样直击参数膨胀焦虑,也不像“RAG到底该…

作者头像 李华