db::listen() 在 tp5.1 中无效,必须用 db::getconnection()->setqueryhook() 才能捕获 sql;需在 app/common.php 等初始化阶段注册回调,接收 $sql、$time、$params 三参数,缺一不可,否则无法还原真实执行语句。

Db::listen() 在 TP5.1 中怎么用才有效
TP5.1 不支持 Db::listen() 的全局注册方式(那是 TP6+ 的特性),必须用 think\db\Connection::setQueryHook() 才能捕获 SQL。直接写 Db::listen(...) 会静默失败,日志里啥都不出现。
实操建议:
- 在应用初始化阶段(比如
app/common.php或数据库连接创建后)调用:Db::getConnection()->setQueryHook(function($sql, $time, $params) { ... }); -
$sql是带问号占位符的原始语句,$params是绑定参数数组,必须一起记录,否则看不出实际执行条件 -
$time是毫秒级耗时,但注意:它只包含 PDO 执行时间,不含网络延迟、锁等待等真实 DB 层耗时 - 别漏掉
if ($time > 500)这类阈值判断——TP5.1 没内置慢查询开关,全靠手动比对
为什么 getLastSql() 查不到真正执行的 SQL
getLastSql() 返回的是“最后构造出的 SQL 字符串”,不是实际发给数据库的那条。尤其在事务、批量操作或用了 insertAll()、where()->update() 时,它可能返回空、旧语句,甚至拼错的 SQL。
常见错误现象:
- 事务中执行了 3 条 UPDATE,
getLastSql()只返回第 1 条 - 用了
cache(true),SQL 根本没进数据库,getLastSql()却有内容 -
where('id', $id)和where('id = '.$id)生成的getLastSql()看起来一样,但后者绕过预编译,执行计划可能完全不同
真正要查“到底执行了什么”,得靠 setQueryHook() 回调里的 $sql + $params 组合还原。
Explain 分析前必须做这三件事
拿 TP 生成的 SQL 去 MySQL 执行 EXPLAIN,结果不准?大概率是漏掉了关键还原步骤。
必须:
- 把
getLastSql()或setQueryHook()拿到的$sql中的?,替换成$params对应的真实值(注意字符串要加引号、数字不加) - 确认 MySQL 版本和字符集与生产环境一致——不同版本优化器对
ORDER BY + LIMIT的处理逻辑可能不同 - 在同库同表结构下执行,避免测试库没建索引、或字段类型(如
VARCHARvsINT)导致隐式转换,让索引失效
重点看 type、key、rows 三列:type=ALL 或 key=NULL 是硬伤;rows 比实际结果大几十倍,基本等于没走好索引。
慢查询日志别只信 ThinkPHP 层
TP5.1 日志里写的 [RunTime:0.002148s] 是 PHP 层计时,不是数据库真实耗时。真正的慢查询源头,90% 在 MySQL 配置没开、阈值设太高、或没配 log_queries_not_using_indexes=ON。
检查顺序必须是:
- 先连 MySQL 执行
SHOW VARIABLES LIKE 'slow_query_log';,确认返回ON - 再查
long_query_time,TP5.1 默认不干预这个值,生产环境设1.0,调试期可临时SET GLOBAL long_query_time = 0.5; - 最后看慢日志文件路径是否可写:
ls -l /var/log/mysql/slow.log,MySQL 进程用户(通常是mysql)必须有写权限,否则日志静默丢弃
如果 MySQL 慢日志里一条慢 SQL 都没有,就别在 TP 日志里翻来翻去——问题不在 PHP,而在数据库没“开口说话”。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











