需从连接管理、查询构造、缓存机制和内存使用四层面优化:启用持久连接并调优wait_timeout;用相关子查询替代多重left join避免笛卡尔积;开启数据库查询缓存并规范缓存范围;将通用类注册为共享服务实现单例复用;严禁视图层执行数据库操作。

提升CodeIgniter应用响应速度、降低服务器负载、避免数据库连接耗尽,需要从连接管理、查询构造、缓存机制和内存使用四个层面切入,不能只依赖单一配置改动。
启用持久连接复用数据库连接
修改 application/config/database.php 中的 pconnect 参数为 TRUE,让PHP进程结束后不销毁连接,而是归还给连接池复用。
这一步必须配合数据库服务端的 wait_timeout 设置(建议 ≥ 300 秒),否则 MySQL 可能主动断开空闲连接,导致下次请求时触发重连失败。
确认生效:在控制器中执行 var_dump($this->db->conn_id);,连续两次请求返回的 resource ID 相同,说明连接被复用了。
用子查询替代多重 LEFT JOIN
当需要对同一张表(如 restaurant_logs)按不同 ntype 值分别统计时,避免写成:
$this->db->select('a.id, b1.count1, b2.count2, b5.count5')->from('orders a')->join('restaurant_logs b1', 'b1.order_id = a.id AND b1.ntype = 1', 'left')->join('restaurant_logs b2', 'b2.order_id = a.id AND b2.ntype = 2', 'left')->join('restaurant_logs b5', 'b5.order_id = a.id AND b5.ntype = 5', 'left');
应改用相关子查询方式:
第一步:在模型方法中构建主查询 → 第二步:为每个统计维度单独写一个子查询 → 第三步:用 SELECT 子句内嵌这些子查询,例如:
$this->db->select("id, (SELECT COUNT(*) FROM restaurant_logs WHERE order_id = orders.id AND ntype = 1) AS count1, (SELECT COUNT(*) FROM restaurant_logs WHERE order_id = orders.id AND ntype = 2) AS count2");
这种写法避免了笛卡尔积膨胀,【执行计划更可控,尤其在 restaurant_logs 表超过 10 万行时,查询耗时可下降 60% 以上】。
启用数据库查询缓存
在 application/config/database.php 中设置:'cache_on' => TRUE,并确保 'cachedir' 指向一个可写的绝对路径目录(如 /var/www/cache/db/)。
缓存仅对 get()、get_where() 等读取类查询生效,写操作(insert()、update())会自动清空对应表的全部缓存。
注意:不要对含 NOW()、RAND() 或用户 session 数据的查询启用缓存,否则返回结果会错乱。
将通用功能类注册为共享服务
方法一:在 app/Services.php 中添加服务定义:
public static function dataFormatter($getShared = true) { if ($getShared) { return new \App\Libraries\DataFormatter(); } return new \App\Libraries\DataFormatter(); }
方法二:在控制器中调用 service('dataFormatter') 获取实例,而非 $this->load->library('dataFormatter')。
区别在于:每次 load->library() 都新建对象,而 service() 返回的是单例——【同一请求生命周期内,无论调用多少次 service('dataFormatter'),都只初始化一次对象】。
禁用视图层数据库操作
检查所有 .php 视图文件,删除类似 $this->db->query("SELECT ...") 或 \Config\Database::connect() 的代码。
把菜单、侧边栏、用户状态等需动态加载的数据,统一移至控制器或服务层预处理完成,再以关联数组形式传入视图。
若视图中存在循环渲染且每次循环都查一次数据库(N+1 问题),性能会随数据量线性恶化,必须重构。











