查看服务器日志中的SQL语句,核心思路是分清日志来源:应用日志直接打印SQL、数据库通用日志记录所有查询、慢查询日志记录低效语句,然后针对不同来源用tail、grep、awk等命令精准过滤。实际操作中,多数线上问题的排查都离不开这三类日志的组合使用,下面按照从易到难的顺序,拆解每一步怎么做。
应用日志里的SQL语句怎么看
服务器上最常见的SQL日志来源是业务应用自己打印的,比如Java的log4j、Python的logging、Node.js的console,开发时为了方便调试,经常在代码里把执行的SQL拼进日志输出,这类日志通常没有固定格式,但有个共性:SQL关键字(SELECT、INSERT、UPDATE、DELETE)会和参数、执行时间、调用堆栈混在一起。
先用grep快速定位SQL行
假设你的应用日志放在/var/log/app/order.log,想找所有包含”SELECT”的语句,直接执行:
grep "SELECT" /var/log/app/order.log
不过实际日志里SQL可能跨行,或者小写,更稳妥一点的做法是先看日志文件长什么样,再决定过滤规则:
tail -100 /var/log/app/order.log
看完格式后,用grep -i忽略大小写,用-n显示行号方便回看上下文:
grep -ni "select|insert|update|delete" /var/log/app/order.log
用tail配合grep实时追踪
线上排查时,往往需要一边看日志一边复现问题,用tail -f实时输出新写入的日志,再用管道交给grep过滤,只留下SQL相关行:
tail -f /var/log/app/order.log | grep --line-buffered -i "sql"
--line-buffered参数保证每行日志立刻被grep处理,而不是攒一批才输出,避免实时性变差。
用awk提取SQL参数
应用日志里的SQL常常是”预编译语句+参数列表”的形式,
[INFO] execute SQL: SELECT FROM user WHERE id = ? params: [123]
如果你想把实际参数替换进SQL里,用awk处理更灵活,先按params:分隔,再拼接:
awk -F'params:' '{print $1 " " $2}' /var/log/app/order.log
这步不一定每个场景都适用,但能帮你快速看到SQL里的具体值,而不是占位符,业内专家指出,定位线上慢查询时,参数值往往比语句本身更有排查价值,因为同样的SQL可能因不同参数走不同执行计划。
数据库日志怎么开启和查看
应用日志不一定记录完整SQL,尤其是用了ORM框架时,可能只打了一行debug信息,真正可靠的SQL日志来源是数据库自己,以MySQL为例,它提供了两种日志:通用查询日志(general_log)和慢查询日志(slow_query_log),行业共识认为,生产环境建议优先开慢查询日志,通用日志因为记录所有查询,对性能影响较大,只建议短时间开启。
mysql慢查询日志怎么看
先确认当前是否开启:
mysql -e "SHOW VARIABLES LIKE 'slow_query_log%';"
如果看到OFF,临时开启(重启失效):
SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1;
设置long_query_time=1表示超过1秒的SQL会被记录,线上环境通常从1秒开始调,如果日志量过大再逐步提高阈值,开启后查看日志文件路径:
mysql -e "SHOW VARIABLES LIKE 'slow_query_log_file';"
查询结果会给出类似/var/lib/mysql/server-slow.log的路径,然后用之前的grep、tail命令分析,慢查询日志每行格式固定,包含执行时间、用户、主机、执行耗时、锁等待时间、返回行数等字段。
通用日志怎么开
通用日志记录所有客户端收到的SQL,排查”某个语句到底发到数据库没有”时非常有用:
SET GLOBAL general_log = 'ON'; SET GLOBAL general_log_file = '/tmp/general.log';
注意:通用日志会记录包括密码在内的明文SQL,开启后务必及时关闭,并清理日志文件,避免敏感信息泄露。
用mysqldumpslow汇总慢查询
慢查询日志如果积累很多,一条条看效率低,MySQL自带mysqldumpslow工具,能按执行次数、耗时等维度汇总:
mysqldumpslow -s at -t 10 /var/lib/mysql/server-slow.log
s at表示按平均时间排序,t 10只显示前10条,这个工具会把SQL里的具体参数用N代替,方便把同类语句归在一起,比手工翻日志快得多。
日志里找不到SQL时怎么办
有时候应用日志和数据库日志里都没有SQL,常见原因有三个:日志级别设置过高、用了连接池缓存、ORM框架未开启SQL输出,逐个排查:
- 应用日志级别调成DEBUG,找到打印SQL的那行logger,确认它没有被过滤。
- 如果使用的是MyBatis、Hibernate等框架,检查配置是否开启了
showSql,MyBatis需要设置mybatis.configuration.log-impl=StdOutImpl,Hibernate需要设置hibernate.show_sql=true。 - 连接池(如Druid、HikariCP)有时会拦截SQL日志,Druid的StatFilter可以配置
logSlowSql=true,把慢SQL打到独立日志里。
还有一种情况:SQL根本不是应用发的,而是定时任务或运维脚本通过命令行直接执行的,这时要去查数据库的processlist或binlog。
用SHOW PROCESSLIST抓正在执行的SQL
如果你怀疑有某个SQL正在运行,但日志里没记录,直接连上数据库执行:
SHOW FULL PROCESSLIST;
结果里的Info字段就是当前正在执行的SQL语句,连续执行几次,能看到SQL的执行状态和耗时。
用binlog反查历史SQL
MySQL的binlog(二进制日志)记录了所有更改数据的操作,但默认是二进制格式,需要解析,使用mysqlbinlog工具:
mysqlbinlog --base64-output=decode-rows -v /var/lib/mysql/binlog.000123
输出里能还原出INSERT、UPDATE、DELETE的具体行数据,不过格式比较繁琐,适合在误删数据或审计场景下使用,日常排查SQL语句不常用。
日志量太大时怎么高效过滤
日志文件动辄上百MB,直接grep全文件会卡顿,先缩小范围,再组合命令。
按时间范围截取
如果日志按天滚动,直接指定文件路径即可,如果是单文件,用sed截取某个时间段,假设日志时间戳是2026-03-10 10:00:00,想截取10点到11点:
sed -n '/2026-03-10 10:/,/2026-03-10 11:/p' /var/log/app/order.log
按关键字和多条件过滤
先grep出SQL行,再用第二个grep筛选特定表或条件:
grep "SELECT" /var/log/app/order.log | grep "FROM user"
如果SQL是多行拼接的,grep默认一次只处理一行,可以用grep -Pzo配合正则匹配跨行模式,例如匹配SELECT ... ;直到分号结束:
grep -Pzo "(?s)SELECT.?;" /var/log/app/order.log
用tail + head精确取中间段
文件很大又想看中间的某段时,先用wc -l数行数,再用tail取后半段,head截取前半段,比如文件有10万行,看第5万到第5万5千行:
tail -n +50000 /var/log/app/order.log | head -n 5000
这个组合比sed在大文件上更快,因为tail不会从头扫描整个文件。
不同场景下看SQL日志的实际操作路径
场景不同,看SQL日志的命令组合也会变化,下面给几个典型场景的完整操作过程。
排查接口响应慢:先查慢查询日志,按时间排序看顶部的慢SQL;如果没结果,再查应用日志中该接口的SQL记录,统计单条SQL耗时;最后用SHOW PROCESSLIST实时观察。
复现”偶发报错”:开启MySQL通用日志,同时用tail -f监控应用日志中的异常堆栈,两边时间戳对照,找到报错前最后一条SQL,注意通用日志只能开一小会儿,复现后立刻关闭。
分析SQL是否走索引:拿到SQL后,用EXPLAIN语法手动执行:
EXPLAIN SELECT FROM order WHERE status = 'PAID';
看type字段,如果是ALL说明全表扫描,需要优化索引。
清理日志文件:日志增长过快时,不要直接删文件,容易让进程持有文件句柄,用> file.log
> /var/log/app/order.log
或者配置logrotate自动切割。
Q&A:关于服务器SQL日志的常见疑问
服务器log里只有报错信息,没有完整SQL怎么办?
多数情况下,应用框架会隐藏SQL细节,建议先检查日志配置里的SQL打印开关,比如Python的SQLAlchemy需要设置echo=True,Java的MyBatis需要配置日志实现,如果实在无法打印,就用数据库的通用日志或binlog补充,但需要在测试环境操作,避免影响线上性能。
慢查询日志和通用日志有什么区别,该用哪个?
慢查询日志只记录超过指定阈值的SQL,适合长期开启做性能监控,通用日志记录所有SQL,信息完整但开销大,适合短时间排查特定问题,生产环境基本只开慢查询,通用日志很少常开。
查询日志里的SQL参数是问号,怎么还原真实值?
这是预编译语句的典型表现,慢查询日志里# User@Host和# Query_time字段后面的语句会用占位,要还原,需要结合应用日志里的参数列表,或者使用MySQL的performance_schema中的events_statements_history_long表,它能记录完整的预编译SQL和参数,据MySQL官方文档,该表需要先开启相关消费者。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/691843.html





