面对 ThinkPHP6 查询慢的问题,最稳妥的做法是在开发环境开启应用调试日志定位 SQL,而在生产环境优先依赖数据库自身的慢查询日志,避免应用层记录带来的额外开销。
先说结论:不要直接在生产环境开启 ThinkPHP 的全量 SQL 日志,应先用数据库慢查询日志锁定问题语句,再结合 EXPLAIN 分析索引。
- 先定位:确认慢查询是偶发还是常态,区分是应用层逻辑还是数据库层瓶颈。
- 先做:开启 MySQL 慢查询日志或临时开启 TP 调试模式抓取特定 SQL。
- 再验证:通过 EXPLAIN 分析执行计划,添加索引后对比执行时间。
ThinkPHP6 配置 SQL 日志
ThinkPHP6 的 SQL 日志机制不仅依赖全局调试开关,还需确保数据库连接配置启用了调试。仅修改 `.env` 中的 `APP_DEBUG` 可能不足以记录详细 SQL 执行信息。
1. 修改 .env 文件(开发环境)
APP_DEBUG=true2. 配置 database.php(关键步骤)
检查 `config/database.php` 文件,确保对应连接配置中 `debug` 参数为 `true`。这是框架记录 SQL 日志的直接开关。
return [
// 默认数据库连接配置
'default' => 'mysql',
'connections' => [
'mysql' => [
// 其他配置...
'debug' => true, // 开启 SQL 日志记录
],
],
];配置生效后,日志文件通常位于 `runtime/log` 目录下,可通过 `tail -f` 实时查看。
MySQL 慢查询日志配置
生产环境建议通过数据库层面记录慢 SQL。根据数据库部署方式不同,配置方法有所区别。
1. 自建数据库(修改 my.cnf)
编辑 MySQL 配置文件(通常位于 `/etc/my.cnf` 或 `/etc/mysql/my.cnf`),在 `[mysqld]` 段落下添加以下配置以实现永久生效:
[mysqld]
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 2
log_queries_not_using_indexes = 1修改后需重启 MySQL 服务:`systemctl restart mysqld`。
2. 云数据库(RDS/Aurora 等)
云数据库实例通常禁止执行 `SET GLOBAL` 命令。需在云厂商控制台找到“参数组”或“实例配置”,修改 `slow_query_log` 和 `long_query_time` 参数后重启实例或应用参数。
索引优化实操
从慢查询日志中提取执行时间超过阈值的 SQL 语句,使用 `EXPLAIN` 分析执行计划。重点关注 `type` 是否为 `ALL` 或 `index`,以及 `key` 是否为 NULL。
EXPLAIN SELECT * FROM orders WHERE user_id = 1001;若发现缺失索引,根据查询条件字段添加索引。以下是添加普通索引和联合索引的 SQL 示例:
-- 添加单列索引
ALTER TABLE orders ADD INDEX idx_user_id (user_id);
-- 添加联合索引(注意最左前缀原则)
ALTER TABLE orders ADD INDEX idx_user_status (user_id, status);注意:对百万级以上数据量的表执行 `ALTER TABLE` 可能会锁表,建议在业务低峰期操作,或使用在线 DDL 工具(如 pt-online-schema-change)。
验证优化效果
优化完成后,通过以下方式确认效果:
- 日志对比:观察数据库慢查询日志中,该条 SQL 是否不再出现或执行时间显著降低。
- 执行计划:再次使用 `EXPLAIN` 命令,确认 `key` 字段已显示为新添加的索引名,且 `rows` 扫描行数减少。
- 应用监控:在 ThinkPHP 调试模式下,查看页面底部或日志中该次查询的 `RunTime` 数值变化。
常见风险与坑
- 生产环境误开调试:切勿将 `APP_DEBUG=true` 或数据库 `debug => true` 部署到生产环境,这不仅影响性能,还可能泄露数据库结构等敏感信息。排查完毕后务必关闭。
- 云数据库权限限制:在云数据库上执行 `SET GLOBAL slow_query_log = 'ON'` 通常会报错(ERROR 1227),请改用控制台参数组配置。
- 日志磁盘爆满:开启慢查询日志后,若不限制日志文件大小或定期清理,可能占满磁盘空间。建议配置日志轮转(log_rotate)。
- 索引失效场景:即使加了索引,如果查询条件使用了函数、模糊查询 `%` 在前或类型隐式转换,索引依然可能失效,需结合 `EXPLAIN` 具体判断。
参考来源
- ThinkPHP 6.0 官方文档,数据库配置与调试说明,https://doc.thinkphp.cn/
- MySQL 官方文档,Slow Query Log 配置说明,https://dev.mysql.com/doc/