优先选 pdo:支持多数据库切换、错误默认抛异常、预处理更可靠、命名参数更清晰;仅老系统维稳或强依赖 mysql 特性时选 mysqli,且字符集与错误模式配置不可省略。

CodeIgniter 默认支持 mysqli 和 PDO 两种数据库驱动,选哪个不取决于“哪个更新”,而取决于你的项目实际约束和长期维护成本。
优先选 PDO:绝大多数新项目和可演进系统
CodeIgniter 3 和 4 都原生支持 PDO,并且推荐在配置中显式指定 dbdriver = 'pdo' 并搭配 MySQL DSN(如 mysql:host=localhost;dbname=test;charset=utf8mb4)。原因很实在:
- 切换数据库几乎零代码改动:今天用 MySQL,明天想换 SQLite 做单元测试,或迁到 PostgreSQL,只需改一行 DSN,模型和查询逻辑全都不动
- 错误默认抛异常:PDO 配合
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,连接失败、查询出错都会立即中断并报错,不会静默吞掉故障 - 预处理行为更一致:只要关闭模拟预处理(
PDO::ATTR_EMULATE_PREPARES = false),参数真正交由 MySQL 服务端编译,防注入更可靠;而 mysqli 的 prepare 在连接重连等边界场景下可能退化,不易察觉 - 命名参数支持更清晰:比如
WHERE status = :status AND created_at > :since,可读性与维护性明显优于 mysqli 的问号顺序绑定
选 MySQLi:老系统维稳或强依赖 MySQL 特性
以下情况才建议保留或选用 mysqli:
- 正在维护一个 CI 3 老项目,已大量使用
$this->db->query("...")拼接 SQL,且短期内无法重构为预处理 —— mysqli 至少比废弃的mysql_*扩展安全得多 - 明确要用 MySQL 8.0+ 独有功能,比如异步查询(
mysqli::poll())、角色管理、不可见索引,且确认永不跨库 - 团队对面向过程风格更熟悉,且项目无迁移计划,追求最小学习成本上线
注意:CodeIgniter 内部对 mysqli 的封装已屏蔽大部分底层细节,但若你手动绕过 Query Builder 直接调用原生 mysqli 对象,就需自行处理 set_charset("utf8mb4") 和 connect_error 检查 —— 这些在 PDO 驱动里已被框架层更稳妥地覆盖。
字符集与错误模式必须显式配对
无论选哪个驱动,这两项配置都不可省略,且顺序关键:
-
PDO:DSN 中写
charset=utf8mb4+ 实例化后立刻setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)+setAttribute(PDO::ATTR_EMULATE_PREPARES, false) -
MySQLi:new mysqli() 后立刻
$mysqli->set_charset("utf8mb4"),再紧跟if ($mysqli->connect_error) die(...)或提前设mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT)
漏掉任一环节,都可能导致中文乱码、注入绕过或线上静默失败。
别被“性能”误导,真实瓶颈不在驱动层
有人认为 mysqli “原生更快”,但实测中,驱动差异带来的毫秒级开销,在 Web 请求链路(网络延迟、PHP 解析、模板渲染、IO 等)里占比微乎其微。真正拖慢系统的,往往是没加索引的查询、N+1 查询、未复用的连接,或错误的事务粒度 —— 这些问题和你用 PDO 还是 mysqli 无关。
现代 CodeIgniter 项目,应把精力放在 Query Builder 合理使用、模型职责收敛、以及日志与监控覆盖上。驱动只是底座,选 PDO 是为了底座更稳、更薄、更易替换。











