ci3默认pconnect=true是设计机制而非bug,通过复用相同凭据的连接提升性能,但需协同调低mysql wait_timeout并避免未提交事务或表锁,否则导致连接堆积。

因为 CI3 默认开启的 pconnect = TRUE 是永久连接(Persistent Connection),它不随 PHP 脚本结束而关闭,而是被保留在连接池中供后续请求复用。这不是 bug,是设计机制——但若应用逻辑未正确管理或数据库端未设合理超时,就会导致连接长期挂起、堆积甚至耗尽。
永久连接的本质是复用,不是“一直活着”
PHP 每次发起永久连接请求时,会先检查是否存在相同用户名、密码、主机、端口的空闲连接;有则复用,无则新建并保留。脚本执行完,连接不会销毁,而是归还给连接池等待下一次调用。
- 同一进程内多次调用
$this->db,很可能复用同一个底层 MySQL 连接 - 不同请求(如并发用户)可能共享该连接,也可能各自持有独立的永久连接
- 连接是否真正“释放”,取决于 MySQL 服务端的
wait_timeout和应用层是否主动断开
为什么看起来“不释放”?关键在两端脱节
常见表现是 SHOW PROCESSLIST 中大量 Sleep 状态连接长期存在。根本原因往往是 MySQL 服务端与 PHP 应用对连接生命周期的预期不一致:
- MySQL 默认
wait_timeout = 28800(8 小时),远长于 Web 请求生命周期 - CI3 不会在脚本结束时调用
mysqli_close()或PDO::close()—— 它依赖服务端超时回收 - 如果服务端超时过长,又没其他机制清理,连接就“卡住”了
哪些情况会让连接更难释放?
永久连接本身没问题,但以下操作会加剧连接滞留甚至泄漏:
- 事务未提交或回滚(
START TRANSACTION后没COMMIT/ROLLBACK),连接会被持续占用 - 使用了表锁(
LOCK TABLES)但未解锁,后续复用该连接的请求会被阻塞 - CI3 模型中手动执行原生查询却未处理异常路径,导致连接未被重置或释放
- 多数据库配置中混用
$this->load->database('xxx')和$this->load->database('xxx', TRUE),造成实例混乱、连接归属不清
怎么让连接更可控?
不建议直接关掉 pconnect(性能损失明显),而是通过协同配置让永久连接“健康流动”:
- MySQL 端:把
wait_timeout降到 60~300 秒,执行SET GLOBAL wait_timeout = 180;并写入my.cnf - CI3 配置中确保
char_set和dbcollat明确(如utf8mb4),避免因字符集协商失败导致连接异常中断 - 避免在模型中长期持有连接句柄;不在循环里反复
$this->db->reconnect()或手动mysqli_connect() - 上线前用
mysqladmin -u root -p extended-status | grep Threads_connected监控连接数趋势,确认是否线性增长











