CodeIgniter 查询慢应先用 EXPLAIN 分析SQL而非修改PHP代码;重点检查type(避免ALL/index)、key(是否为NULL)、索引类型(单列/联合/函数索引);禁用SELECT *,显式指定字段以利用覆盖索引;事务中慎用get()避免锁表。

CodeIgniter 查询慢,先看 EXPLAIN 而不是改 PHP 代码
绝大多数人遇到 CodeIgniter 查询变慢,第一反应是调 $this->db->query() 或加缓存,但真正瓶颈常在 MySQL 层。不看执行计划,优化就是蒙的。
实操建议:
- 在开发环境开启 MySQL 慢查询日志,或直接对慢 SQL 手动加
EXPLAIN前缀(如EXPLAIN SELECT * FROM users WHERE email = 'x@y.z') - CodeIgniter 本身不提供内置
EXPLAIN封装,需手动拼接:用$this->db->last_query()拿到刚执行的 SQL,再套上EXPLAIN发给数据库 - 重点看
type字段:出现ALL(全表扫描)或index(索引全扫)基本等于没走有效索引;ref或range才算正常 - 注意
key列是否为NULL——哪怕写了WHERE status = 1,如果status没建索引,key就是空的
WHERE 条件字段没建索引?CodeIgniter 不会自动帮你补
CodeIgniter 的 where()、or_where()、like() 等方法只是拼 SQL,它不管字段有没有索引。你写得再优雅,数据库照样全表扫。
常见错误现象:
-
$this->db->where('created_at >', '2023-01-01')->get('logs')很慢 →created_at没索引 -
$this->db->like('title', 'php', 'both')返回慢 →LIKE '%php%'无法用 B-tree 索引,除非用全文索引或fulltext - 复合查询如
WHERE category_id = 5 AND status = 1 ORDER BY created_at DESC→ 单独给category_id建索引没用,得建联合索引(category_id, status, created_at)
参数差异提醒:MySQL 8.0+ 支持函数索引,但 CodeIgniter 不生成 CREATE INDEX ON users ((UPPER(email))) 这类语句,必须手动建。
select() 别偷懒写 *,尤其关联多表时
CodeIgniter 默认允许 $this->db->select('*'),但这是性能杀手,特别是 JOIN 多张表后返回几十个字段,其中 90% 根本不用。
为什么这样做:
- 网络传输量翻倍:比如用户表 + 订单表 JOIN,
*可能带出重复的user_id、大字段description、json_data等 - 内存占用高:PHP 把所有字段读进数组,即使只取
id和name,也得加载整行 - 索引覆盖失效:如果只查
id和name,而你建了联合索引(id, name),那用SELECT id, name就能走“覆盖索引”,不用回表;但SELECT *一定触发回表
实操建议:
- 显式写出要用的字段:
$this->db->select('u.id, u.name, o.order_no') - 避免在
SELECT中用函数或表达式(如CONCAT(first_name, ' ', last_name)),它们会让索引失效且增加 PHP 解析负担 - 用
$this->db->get_compiled_select()预览最终 SQL,确认字段数和别名是否合理
事务里嵌套 get() 和 update() 容易锁表
CodeIgniter 的 trans_start() / trans_complete() 看似简单,但混合读写操作时,MySQL 默认隔离级别(REPEATABLE READ)下可能引发间隙锁或死锁。
容易踩的坑:
-
$this->db->where('status', 'pending')->get('orders')在事务中执行后立刻update()同一批记录 → 可能被其他并发请求阻塞,甚至超时 - 没指定
FOR UPDATE却期望“读完就锁住”:CodeIgniter 不支持在get()后自动加锁,得手写$this->db->query("SELECT * FROM orders WHERE ... FOR UPDATE") - 长事务(比如循环里多次
get()+insert())导致锁持有时间过长,拖慢整个库
性能影响明显:一个 10 行的事务若含 3 次无谓 get(),可能比单条 UPDATE ... WHERE 慢 5 倍以上,因为每次 get() 都要走一次网络往返 + 结果集解析。
复杂点在于:索引优化解决不了锁问题,得靠业务逻辑拆分、减少事务粒度,或者改用乐观锁(比如加 version 字段)。这点很多人忽略,以为建好索引就万事大吉。










