PHP 8.1 网站数据库查询变慢、页面加载卡顿,90% 情况下是 MySQL 索引未生效导致全表扫描,须用 EXPLAIN 验证执行计划,重点检查 key 是否为 NULL、type 是否为 ALL、rows 是否远超预期。

PHP 8.1 网站数据库查询变慢、页面加载卡顿,90% 情况下不是 PHP 版本问题,而是 MySQL 索引没生效导致全表扫描——必须用 EXPLAIN 直接看执行计划,不能靠猜测或日志。
第一步:确认是否真没走索引
在 Laravel 开发环境控制器中插入两行调试代码:
DB::enableQueryLog();
$users = User::where('email', 'test@example.com')->get();
dd(DB::getQueryLog());
找到输出里那条 SELECT SQL,复制出来,在 phpMyAdmin 或命令行中执行:EXPLAIN 开头的完整语句。重点盯住三列:key(是否为 NULL)、type(是否为 ALL)、rows(是否远超预期)。
立即学习“PHP免费学习笔记(深入)”;
只要 key 是 NULL 或 type 是 ALL,就坐实了索引失效。
第二步:快速定位八类高频失效场景
方法一:函数包裹字段(最常见)
比如写 WHERE DATE(created_at) = '2025-09-26',哪怕 created_at 有索引也白搭。MySQL 必须对每一行计算 DATE() 才能比对,无法跳过扫描。【修复动作:把函数从字段侧移到常量侧】 改成 created_at >= '2025-09-26 00:00:00' AND created_at
方法二:隐式类型转换
user_id 字段是 INT 类型,但代码里写了 ->where('user_id', '100'),字符串参数触发自动转换,索引直接失效。必须保证类型完全一致:【传入整数 100,而不是字符串 '100'】。
方法三:联合索引未用最左列
建了 INDEX (status, created_at),但查询只写了 ->where('created_at', '>', now()),跳过了 status,这个索引就完全被忽略。要么补上 status 条件,要么单独为 created_at 建单列索引。
第三步:验证修复是否真正生效
① 修改 SQL 后,再次执行 EXPLAIN,确认 key 显示索引名、type 变为 ref/range、rows 从十万级降到几百以内;
② 在 Laravel 中用 DB::listen() 拦截真实执行的 SQL,确保框架生成的语句和你手动测试的一致;
③ 如果用了 whereRaw,务必检查参数是否用数组传递,严禁字符串拼接——【拼接会导致 SQL 注入且可能破坏索引】;
④ 对于模糊查询 name LIKE '%关键词%',普通 B+ 树索引无效,必须改用 FULLTEXT 索引加 MATCH AGAINST 语法。



















