codeigniter 自定义类重复加载数据库的本质是类实例化失控与数据库连接未复用,须从类注册方式、连接生命周期、调用上下文三方面解决:改用共享服务注册、依赖注入db实例、禁用load->library、启用持久连接。

CodeIgniter 自定义类重复加载数据库,本质是类实例化失控 + 数据库连接未复用,不是“多加几行代码”能解决的。核心得从类注册方式、连接生命周期、调用上下文三处动手。
自定义类里反复 new Database() 导致连接爆炸
很多自定义类(比如 OrderService、ReportGenerator)习惯在 __construct() 里手动实例化数据库:$this->db = \Config\Database::connect();。这会绕过 CI 的连接池管理,每次 new 一个类就新建一条 MySQL 连接。
- 确认是否真需要独立连接:95% 的场景下,用框架已有的
$this->db实例就够了,它默认就是共享的 - 若必须隔离连接(如读写分离),改用
\Config\Database::connect('slave')显式指定组名,别直接 new - CI4 中禁止在构造函数里调用
connect()后不缓存——否则每次请求都新建资源,max_connections很快打满
CI4 中把自定义类注册为共享服务而非普通 Library
CI3 的 $this->load->library() 每次调用都 new 一次;CI4 的服务机制默认返回单例,这才是防重复加载的正解。
- 在
app/Services.php中添加服务定义:public static function orderService($getShared = true) - 函数体内用
return $getShared ? new OrderService() : new OrderService();不行——要改成return service('orderService', $getShared);并配合shared()声明 - 关键点:服务类构造函数里不要自己连 DB,而是通过依赖注入接收
\CodeIgniter\Database\BaseConnection实例 - 这样整个请求生命周期内,
service('orderService')返回的永远是同一个对象,DB 实例也只初始化一次
控制器里别用 $this->load->library() 加载自定义类
哪怕你用了 CI4,如果还在控制器里写 $this->load->library('OrderService'),那就等于主动退回到 CI3 的非共享模式——框架根本不会走 Services.php 的逻辑。
- CI4 中彻底弃用
load->library()加载自定义类,它只兼容旧代码,不触发服务注册 - 统一改用
service('orderService')或service('orderService', false)(获取非共享实例) - 若需在视图中调用,先在控制器里
$data['orderSvc'] = service('orderService');再传入,别在 view 里现场调 service() - 检查所有
app/Controllers下的文件,grepload->library,替换成 service 调用
数据库配置开启 pconnect 且 wait_timeout 匹配
即使类不重复实例化,底层连接仍可能因配置不当被频繁重建。这是最容易被忽略的底层支撑点。
- 确认
app/Config/Database.php中'pconnect' => true已启用 - MySQL 侧的
wait_timeout必须 ≥ 300 秒,否则空闲连接会被服务端断开,下次请求时 CI 会误判为失效而新建 - 验证是否生效:在控制器中连续两次调用
var_dump($this->db->conn_id);,resource ID 应该一致 - CI4 默认使用 PDO,若项目混用 mysqli 驱动,需额外确认其
pconnect支持状态(部分旧版本不兼容)
真正卡住性能的往往不是类本身,而是每个类背后悄悄新建的数据库连接和未释放的资源句柄。共享服务 + 正确连接复用 + 彻底弃用旧式加载,这三步缺一不可。尤其注意 CI4 的 service() 和 CI3 的 load->library() 是两套互不兼容的机制,混用会导致看似加载成功、实则每次都 new 的陷阱。











