应启用mysql慢查询日志并配置合理阈值,用pt-query-digest分析日志,结合thinkphp上下文注入与explain执行计划验证,再通过db::listen捕获慢sql事件。

如果您在 ThinkPHP 应用中观察到数据库响应迟缓,但无法定位具体慢 SQL,则很可能是慢查询未被有效捕获与分析。以下是针对 ThinkPHP 环境下慢查询日志的系统性分析方法:
一、启用 MySQL 原生慢查询日志并配置阈值
MySQL 层面的日志是分析慢查询最权威、最低侵入性的源头。必须确保其开启并合理配置,否则上层 PHP 日志无法替代其完整性与准确性。
1、登录 MySQL 执行 SHOW VARIABLES LIKE 'slow_query_log';,确认返回值为 ON;若非 ON,则执行 SET GLOBAL slow_query_log = ON;
2、检查当前慢查询阈值:SHOW VARIABLES LIKE 'long_query_time';,默认为 10 秒,开发/调试环境建议设为 1 或 0.5
3、临时调整阈值:SET GLOBAL long_query_time = 1;;如需永久生效,需在 my.cnf 的 [mysqld] 段落中添加:slow_query_log=ON、long_query_time=1、slow_query_log_file=/var/log/mysql/slow.log
4、验证日志文件路径是否可写:ls -l /var/log/mysql/slow.log,确保 MySQL 进程用户(如 mysql)具有写权限
二、使用 pt-query-digest 工具聚合分析慢日志
原生慢日志格式松散、重复多、无统计维度,直接人工排查效率极低。pt-query-digest 可自动归类 SQL 模板、排序耗时占比、识别高代价操作模式。
1、安装工具:Ubuntu/Debian 执行 apt install percona-toolkit;macOS 执行 brew install percona-toolkit
2、执行分析命令:pt-query-digest /var/log/mysql/slow.log
3、重点关注输出顶部的 Profile 表格,按 Query_time 总耗时降序排列,首行即为最大性能瓶颈
4、逐条审视关键字段:Rows_examined 远大于 Rows_sent 表明存在索引缺失或全表扫描;Lock_time 高则提示锁竞争问题
三、在 ThinkPHP 中注入请求上下文至 SQL 日志
仅靠 MySQL 慢日志无法关联到具体控制器、方法、用户或请求 ID,导致修复无从下手。需在 ThinkPHP 日志中主动嵌入上下文标识,实现 SQL 与业务链路精准映射。
1、在数据库配置 config/database.php 中启用绑定参数记录:'log' => ['level' => ['sql'], 'bind' => true]
2、在全局中间件或 app/bootstrap.php 中,为每条 SQL 日志附加唯一标识:Log::record('SQL Context: ' . request()->url() . ' | UID:' . session('user_id') . ' | TraceID:' . uniqid(), 'sql');
3、若使用 Db 驱动写入日志表,确保日志表(如 think_log)已为 create_time 字段建立索引,避免日志查询自身成为新瓶颈
4、对关键业务 SQL 添加注释标记,例如:Db::table('order')->where('status', 1)->comment('order_list_api')->select();,该注释将出现在 MySQL 慢日志中,便于反向定位
四、结合 EXPLAIN 分析高频慢 SQL 执行计划
获取慢 SQL 后,必须通过 EXPLAIN 验证其执行路径是否合理。同一语句在不同数据分布、索引状态、统计信息下行为可能迥异,不能仅凭经验判断。
1、复制慢日志中的原始 SQL(注意替换 ? 占位符为实际 bind 值,或使用 /*+ QB_NAME(...) */ 注释保留结构)
2、在 MySQL 客户端执行:EXPLAIN FORMAT=TRADITIONAL SELECT ...
3、严格核查三列:type 列不得为 ALL 或 index;key 列必须显示实际使用的索引名;rows 值应显著小于表总行数(理想为 1% 以内)
4、若 Extra 出现 Using filesort 或 Using temporary,表明 ORDER BY 或 GROUP BY 未命中索引,需调整索引或改写查询
五、在 ThinkPHP 内部监听并捕获慢 SQL 事件
ThinkPHP 8 提供了原生的 sql_express 事件机制,可在 SQL 执行完成后实时捕获语句、耗时及执行计划,适用于需要即时告警或写入独立慢日志通道的场景。
1、在 app/bootstrap.php 或服务提供者 register() 方法中调用:Db::listen(function ($sql, $time, $explain) { ... });
2、从配置读取阈值,避免硬编码:$threshold = config('database.slow_sql_threshold', 500);,单位为毫秒
3、判断慢 SQL:if ($time >= $threshold) { ... }
4、写入专用慢日志通道:在 config/log.php 中新增 'slow_sql' 驱动,指定独立路径如 runtime/log/slow_sql/,并禁用 realtime_write 改用异步方式
5、事务内慢 SQL 捕获限制:默认事务中多条 SQL 合并为单事件。如需逐条监控,仅限测试环境启用预处理模拟:'options' => [PDO::ATTR_EMULATE_PREPARES => true]
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











