本文目录导读:

- 第一步:确认日志是否开启及位置
- 第二步:看懂一条日志的核心字段(逐行拆解)
- 第三步:实战解读与排查策略(关键)
- 第四步:常见优化手段(对症下药)
- 第五步:实战案例(举一反三)
- 关键提示:PHP 自身的“慢日志”在哪?
在 PHP 项目中,慢查询日志通常指的是数据库(MySQL)层面的日志,而不是 PHP 自身的日志,解读它的核心目的是精准定位拖慢系统的 SQL 语句,并找到优化的切入点。
以下是系统的解读步骤和实战技巧:
第一步:确认日志是否开启及位置
你需要找到日志文件,在 MySQL 中,通常执行以下命令(如果在命令行):
SHOW VARIABLES LIKE 'slow_query_log%'; SHOW VARIABLES LIKE 'long_query_time';
- slow_query_log:是否开启(
ON或OFF)。 - slow_query_log_file:日志存放的物理路径。
- long_query_time:阈值(默认 10 秒,通常建议调低到 2 秒或 1 秒)。
第二步:看懂一条日志的核心字段(逐行拆解)
假设你从日志中抓取了这样一条记录:
# Time: 2023-10-01T10:23:45.123456Z # User@Host: root[root] @ localhost [127.0.0.1] Id: 12345 # Query_time: 5.234567 Lock_time: 0.002345 Rows_sent: 1000 Rows_examined: 500000 SET timestamp=1696148625; SELECT * FROM orders WHERE user_id = 101 AND status = 'completed' ORDER BY created_at DESC LIMIT 1000;
解读逻辑如下:
| 参数 | 含义 | 健康标准/警惕点 |
|---|---|---|
| Query_time | 总执行耗时(5.23秒) | 这是主角,若远高于阈值,说明 SQL 本身有问题或缺索引。 |
| Lock_time | 锁等待时间(0.002秒) | 通常较小,如果锁时间接近 Query_time,说明并发冲突严重(锁表/锁行)。 |
| Rows_examined | 扫描了多少行(50万行) | 重点排查项,如果这个数字巨大,且 Rows_sent 很小(1000),说明发生了全表扫描,这是主要优化方向。 |
| Rows_sent | 返回了多少行(1000行) | 通常业务需求决定的,如果发送太多,检查是否 SELECT * 或缺少 LIMIT。 |
| SET timestamp | 语句执行的具体时间戳 | 用于定位是业务高峰期还是定时任务导致的。 |
第三步:实战解读与排查策略(关键)
拿到一条日志后,按照以下顺序快速定位病根:
看 Rows_examined 与 Rows_sent 的比例
- 场景 A:
Examined比Sent大 100 倍以上。- 索引失效或缺失。
- 动作:直接对 WHERE 条件字段(
user_id,status)和排序字段(created_at)建立联合索引。
- 场景 B:
Examined与Sent差不多,但Query_time仍然很慢。- 数据量大,但可能是网络传输慢,或者是返回数据行数太多导致序列化时间长。
- 动作:检查是否需要分页(LIMIT),或减少
SELECT的字段(避免SELETE *)。
具体区分“慢”的类型
- 全是
Rows_examined大:优先加索引。 - 全是
Lock_time大:优先看数据库连接池,检查是否有长事务(事务未提交导致锁等待)。 - Query_time 大,但统计值都很小:可能是数据库服务器 CPU 繁忙,或查询涉及复杂的子查询/大表 JOIN。
看时间分布(Time 字段)
- 如果都在下午 2:00 - 3:00 集中出现,可能是定时任务(如数据同步)或报表跑批。
- 如果高并发时出现,通常是缓存失效导致并发穿透,或者慢 SQL 拖垮了数据库连接池。
第四步:常见优化手段(对症下药)
根据解读结果,通常采取以下措施:
- 加索引(最核心):对于
WHERE和ORDER BY的字段,建立复合索引,注意字段顺序(区分度高的放最左)。 - 改写 SQL:避免
SELECT *,避免LIKE '%xx'前置模糊查询,避免在索引列上做函数运算(如LEFT(phone, 3))。 - 分页优化:经典的深分页问题(
LIMIT 500000, 20),可改为WHERE id > 500000 LIMIT 20(延迟关联)。 - 未命中缓存:如果该 SQL 是高频查询且短小,可考虑在 Redis 中缓存结果集,减少数据库压力。
第五步:实战案例(举一反三)
案例日志:
# Query_time: 2.8 Lock_time: 0.0 Rows_sent: 10 Rows_examined: 400000
SELECT * FROM products WHERE category = 'electronics' AND price > 1000;
解读:扫描了 40 万行只给了 10 条,明显缺索引。
优化动作:
ALTER TABLE products ADD INDEX idx_cat_price (category, price);
执行后,Rows_examined 会骤降至 10 行左右,Query_time 下降至 0.01 秒。
关键提示:PHP 自身的“慢日志”在哪?
如果你看到的日志不是这种格式,而是类似于 [pool www] slow request(PHP-FPM 慢日志),那解读逻辑是另一回事,但目前 90% 的 PHP 性能瓶颈都在数据库慢查询,而非 PHP 本身。
补充建议:如果项目使用的是 Laravel 或 Symfony,请开启 DB::listen 事件监听,它可以直接在日志里打印 SQL 和参数,便于快速对应业务代码。