订单日志查询慢主因是数据库索引缺失、未分区或数据堆积,应优先优化SQL和表结构;需用非预处理方式执行EXPLAIN,建立(user_id, created_at)复合索引,超500万行须按月分区,并控制查询粒度、避免SELECT*。
订单日志查询慢,大概率不是 PHP 本身的问题,而是数据库没走索引、日志表缺乏分区或历史数据堆积导致的。直接优化 SQL 和表结构,比改 PHP 代码见效快得多。
EXPLAIN 是否真生效很多同学在 PHP 中用 mysqli 或 PDO 执行 EXPLAIN SELECT ...,但返回空或报错——因为 MySQL 的 EXPLAIN 不支持预处理语句(PDO::prepare() 默认启用)。
PDO::exec() 或 mysqli_query() 直接执行 EXPLAIN SELECT order_id, created_at FROM order_log WHERE user_id = 123 AND created_at > '2025-01-01'
WHERE 条件、ORDER BY 字段对齐,否则 type=ALL 就是全表扫描key 列是否为 NULL,是就说明没走索引order_log 表没复合索引?立刻补上最常用查询路径典型查询如按 user_id + created_at 查最近 30 天日志,但只在 user_id 上建了单列索引,MySQL 无法高效过滤时间范围。
WHERE 字段在前,范围查询字段(如 created_at)放最后 —— ALTER TABLE order_log ADD INDEX idx_user_time (user_id, created_at);
order_id 关联主单,加 order_id 到索引里需谨慎:(user_id, order_id, created_at) 会增大索引体积,且 order_id 通常等值匹配,放中间不如放最后(user_id, created_at),再单独建 (user_id) 就浪费即使加了索引,单表 800 万行时 SELECT COUNT(*) 或 LIMIT 1000,20 仍可能秒级延迟,因为 B+ 树深度变大、缓冲池命中率下降。
RANGE COLUMNS(created_at) 按月分区:ALTER TABLE order_log PARTITION BY RANGE COLUMNS(created_at) (
PARTITION p202512 VALUES LESS THAN ('2025-01-01'),
PARTITION p202501 VALUES LESS THAN ('2025-02-01'),
PARTITION p202502 VALUES LESS THAN ('2025-03-01'),
PARTITION p_future VALUES LESS THAN MAXVALUE
);ALTER TABLE order_log TRUNCATE PARTITION p202512;
created_at BETWEEN ? AND ?,让优化器自动裁剪分区有些接口写成 SELECT * FROM order_log WHERE user_id = ? 然后在 PHP 里用 array_filter 做二次筛选,这是把数据库当内存用。
LIMIT offset, size,且 offset 超过 10 万时改用游标分页(用上一页最后的 created_at 和 id 作为下一页条件)* —— 日志表若有 TEXT 字段,IO 和网络传输成本陡增SET order_log:uid:123 "[{...},{...}]" EX 300,但注意缓存穿透和一致性最常被忽略的一点:日志表的 created_at 字段是不是 DATETIME 类型?如果是 TIMESTAMP 且带时区转换,在 JOIN 或函数包裹(如 DATE(created_at))时会强制类型转换,导致索引失效。查之前先 SHOW CREATE TABLE order_log 看一眼字段定义。