开启thinkphp sql日志需设'debug'=>true、'log_sql'=>true(6.0+)或'trace_sql'=>true(5.1),并配置log.level=['sql'];db::getlastsql()仅返回最后sql字符串,无耗时与参数,应改用db::listen()监听慢查询并记录explain。

如何开启ThinkPHP的SQL日志记录
ThinkPHP默认不记录SQL执行详情,必须手动开启调试模式并配置日志驱动。关键不是加个SHOW PROFILES,而是让框架把每条实际执行的SQL、绑定参数、耗时都落盘。
在app.php或环境配置文件中设置:
'db' => [
'debug' => true, // 必须为true
'log_sql' => true, // ThinkPHP 6.0+ 推荐用这个
'trace_sql' => true, // 旧版本(如5.1)用这个
],
'log' => [
'level' => ['sql'], // 确保sql日志级别被启用
],
注意:debug为false时,log_sql和trace_sql会直接失效——这不是bug,是框架的硬性开关逻辑。
为什么Db::getLastSql()查不到慢查询
Db::getLastSql()只返回最近一次查询的原始SQL字符串,不含执行时间、参数值、是否出错等上下文。它对分析“为什么慢”几乎没用,尤其在事务或批量操作中,容易误判目标SQL。
真正有用的替代方案是监听think\db\Connection的sql_explain事件或使用think\facade\Db::listen()钩子:
Db::listen(function ($sql, $time, $explain) {
if ($time > 1000) { // 超过1秒记为慢查询
Log::write("[SLOW SQL] {$sql} | Time: {$time}ms | Explain: " . json_encode($explain), 'sql');
}
});
-
$time单位是毫秒,注意不是微秒 -
$explain在MySQL下是EXPLAIN结果数组,但仅当db.explain设为true才非空 - 该回调在每次查询结束后触发,包括
insert/update,不只是select
怎么让ThinkPHP自动记录EXPLAIN执行计划
单纯看SQL文本和耗时,无法定位索引缺失或全表扫描问题。EXPLAIN必须显式开启,且只对SELECT语句有效。
在数据库配置中加入:
'db' => [
'explain' => true, // 启用后,每个SELECT都会额外执行一次EXPLAIN
'debug' => true,
],
但要注意三点:
- 开启后QPS明显下降,**切勿在生产环境长期开启**
- 只有
select类查询会触发EXPLAIN,update或delete即使带WHERE也不会解释 - ThinkPHP 6.1+ 的
$explain参数可能为空,需检查MySQL是否支持EXPLAIN FORMAT=JSON,否则降级为传统格式
如何过滤掉ThinkPHP自身日志干扰项
框架在启动、路由解析、配置加载阶段会执行大量内部SQL(比如读取system_config、session表),这些和业务慢查询无关,但混在日志里极易掩盖真问题。
推荐两种过滤方式:
- 按命名空间过滤:在
Db::listen()回调里加判断,例如跳过think\开头的调用栈:debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1)[0]['class'] ?? '' - 按SQL特征过滤:用
stripos($sql, 'SELECT') === 0 && !stripos($sql, 'FROM `think_')排除系统表查询 - 更稳妥的是在日志写入前加业务标识,比如在控制器入口统一设
Log::setTag('biz'),再配合日志通道分离
慢查询分析最常被忽略的点:不是SQL本身写得差,而是同一SQL在不同数据量、不同缓存状态、不同连接池负载下表现差异极大。务必在接近线上数据规模的测试库中复现,而不是只看本地10条测试数据的执行时间。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











