ci4中手动创建的database实例必须显式调用close()释放连接,而通过service('database')获取的实例由框架生命周期管理,不可手动关闭;错误关闭会导致连接泄漏或fatal error。

CI 自定义类里关数据库连接报错,基本是因为没按 CodeIgniter 的生命周期规范来操作——不是“能不能关”,而是“该不该由你来关”。
CI 3.x 中 $this->db 连接不能手动 close()
CodeIgniter 3 默认使用持久连接(pconnect),连接由框架统一管理。调用 $this->db->close() 不仅无效,还可能触发 Fatal error: Call to a member function close() on null 或 mysqli_close(): Connection already closed。
- 框架在脚本结束时自动释放连接,无需、也不应手动干预
-
$this->db->close()实际上只对非持久连接生效,而 CI 3 默认配置是'pconnect' => TRUE - 若强行关闭,后续模型或库中再调用
$this->db就会因实例已销毁而报错
CI 4.x 的 Database 实例必须用 close() 吗?
CI 4 改用依赖注入和连接池管理,$db = \Config\Database::connect() 返回的实例**可以且应该显式关闭**,但前提是:你确实是自己创建的连接,而不是通过服务容器获取的默认实例。
- 从服务容器取的
$db = service('database')—— 不要调close(),它受框架生命周期控制 - 手动新建的连接:
$db = \Config\Database::connect('custom_group')—— 必须在使用后调$db->close(),否则连接泄漏 - 未关闭会导致连接数持续增长,最终触发 MySQL 的
Too many connections错误
自定义类里执行完 SQL 后资源没释放,常见错误写法
典型问题出现在封装了数据库操作的工具类中,比如:
class DbHelper
{
public function query($sql)
{
$db = \Config\Database::connect();
$result = $db->query($sql);
$db->close(); // ✅ 正确(仅限手动 connect)
return $result;
}
}
但下面这些是错的:
- 在构造函数里
$this->db = \Config\Database::connect(),又在析构函数里$this->db->close()—— 可能被多次调用,引发重复 close 报错 - 把
service('database')存为类属性后,在方法里反复调close()—— 破坏框架连接复用机制 - 没检查
$db是否为null就直接调close(),尤其在异常分支中容易遗漏
CI 4 中安全释放连接的推荐写法
用 try/finally 保证连接释放,同时避免重复 close:
$db = \Config\Database::connect('read_slave');
try {
$db->query("SELECT * FROM logs WHERE ts > NOW() - INTERVAL 1 HOUR");
} finally {
if ($db && method_exists($db, 'close')) {
$db->close();
}
}
- 只对明确
connect()出来的实例做 close - 加
method_exists判断,兼容不同驱动(如 SQLite 不一定有close()) - 不依赖
__destruct—— PHP 析构顺序不可控,容易在连接已关后再次触发
真正容易被忽略的是:CI 4 的 service('database') 实例背后是连接池,它的“关闭”其实是归还连接,不是销毁物理连接。混淆这个概念,就会在不该关的地方硬关,或在该关的地方漏关。











