90%以上PHP框架数据库查询变慢源于索引设计不当,应优先为高频SELECT语句建立覆盖索引:按WHERE等值字段、ORDER BY字段、SELECT字段顺序创建联合索引,并用EXPLAIN验证key和Using index是否生效。

PHP框架中数据库查询变慢,90%以上的问题根源不在代码逻辑,而在索引没建对——尤其是该用覆盖索引的地方用了单列索引,该按最左前缀排字段的地方顺序写反了,结果EXPLAIN里还显示“Using filesort”。
确认哪些查询急需覆盖索引
先别急着建索引,打开MySQL慢查询日志或直接在ThinkPHP/Laravel调试栏里找执行时间>500ms的SELECT语句。重点盯三类:
① 查询只返回2~4个字段,但表有20+列;
② WHERE条件固定(如WHERE status = ? AND user_id = ?),且后续总跟ORDER BY created_at DESC;
立即学习“PHP免费学习笔记(深入)”;
③ 频繁调用的列表接口,比如GET /api/orders?user_id=123&status=paid,返回字段只有id, order_sn, amount, created_at。
这三类场景,不加覆盖索引,数据库每次都要先查索引定位行,再回主表捞数据,IO翻倍——【回表是性能杀手,能避免就绝不让它发生】。
用联合索引替代多个单列索引
方法一:按WHERE条件频率排序建复合索引
查用户订单时,80%请求带user_id,60%同时带status,30%再加created_at范围。那就建:CREATE INDEX idx_orders_user_status_time ON orders (user_id, status, created_at);
注意:如果某次查询只用WHERE status = 'paid',这个索引完全失效——因为没从最左字段user_id开始匹配。
方法二:把等值字段放前,范围字段放后
若查询是WHERE category_id = 1 AND price BETWEEN 100 AND 500 AND is_on_sale = 1,必须把category_id和is_on_sale放前面,price放最后:CREATE INDEX idx_prod_cat_sale_price ON products (category_id, is_on_sale, price);
【price是BETWEEN范围查询,它右边的字段无法走索引,顺序错了等于白建】
方法三:用EXPLAIN验证是否命中
执行EXPLAIN SELECT id, order_sn FROM orders WHERE user_id = 100 AND status = 'shipped';,看key列是否显示idx_orders_user_status_time,Extra列是否为Using index——出现这两个,说明覆盖索引生效;若Extra里有Using where; Using filesort,立刻删掉重建。
手动生成覆盖索引SQL语句
第一步:列出查询涉及的所有字段(SELECT + WHERE + ORDER BY)
比如ThinkPHP链式查询:OrderModel::where('user_id', 100)->where('status', 'done')->order('created_at', 'desc')->field('id,order_sn,amount,created_at')->select(); → 字段集为(user_id, status, created_at, id, order_sn, amount)。
第二步:把WHERE等值字段放最前,ORDER BY字段紧随其后,SELECT剩余字段补在末尾
→ 排序后:user_id、status、created_at、id、order_sn、amount。
第三步:生成建索引语句
CREATE INDEX idx_orders_cover ON orders (user_id, status, created_at, id, order_sn, amount);
这一步建完,上面那条查询就彻底脱离主表——所有数据全从索引页读取,连磁盘寻道都省了。



















