sqlite数据库被锁本质是其文件级并发机制限制,主因包括长事务未结束、多线程/进程争写、权限不足或连接泄漏,而非ci4配置问题。

CodeIgniter 4 连接 SQLite 时提示 “database is locked” 或 “file is locked”,本质不是 CI4 的配置问题,而是 SQLite 文件级并发控制机制在起作用——它不支持多进程/多线程高并发写入。CI4 默认使用 PDO SQLite 驱动,但若应用存在长事务、未关闭连接、或多个请求同时写库,就极易触发文件锁。
确认是否真被锁(先排除误判)
很多报错看似是“locked”,实则是权限、路径或连接未初始化导致的假象:
- 检查 Web 服务器用户(如
www-data或apache)对 SQLite 文件及其所在目录有 读写权限(chmod 644 db/app.db+chmod 755 db/) - 确保 DSN 中路径为 绝对路径,且文件已存在(CI4 不自动创建数据库文件)
- 用命令行验证能否手动操作:
sqlite3 /var/www/myapp/writable/database.db "PRAGMA journal_mode;"—— 若报错“unable to open database file”,说明连基础访问都不通,不是锁的问题
关掉持久连接和长事务
SQLite 的锁通常由未结束的写事务或开启的持久连接(pconnect)引发。CI4 的 SQLite 配置中必须禁用这两项:
-
'DBDebug' => true开启调试,观察日志里是否有SQLSTATE[HY000]: General error: 5 database is locked - 在
app/Config/Database.php中,对应 SQLite 组配置里明确设置:'DBPersistent' => false,'DBCache' => false,'DBAutoClose' => true, - 所有写操作(
insert()、update()、delete())务必包裹在显式事务中,并及时$db->transComplete();避免裸调用后不提交也不回滚
规避多请求并发写同一库
SQLite 是单写多读设计,不适合 Web 应用高频并发写场景。真实生产中应主动降级写压力:
- 把日志、统计、缓存类写操作迁出 SQLite,改用文件、Redis 或轻量队列
- 对非关键数据(如访客记录),改用“追加式写入 + 定期合并”策略,减少事务持有时间
- 若必须多点写入,考虑用
WAL(Write-Ahead Logging)模式提升并发读能力:
在首次连接后执行:$db->query("PRAGMA journal_mode = WAL;");
注意:WAL 不能解决写冲突,但能缓解读阻塞写的情况
检查 PHP 层连接泄漏
CI4 使用连接池管理,但若控制器或模型中手动调用 new \CodeIgniter\Database\SQLite3\Connection(...) 或重复 $db->connect(),可能导致底层 SQLite 句柄未释放:
- 一律通过服务容器获取 DB 实例:
$db = \Config\Database::connect('sqlite'); - 避免在循环或回调中反复新建连接
- 部署前运行
php spark test --filter DatabaseTest确认 SQLite 驱动行为正常
不复杂但容易忽略:SQLite 锁问题90%源于写事务没收尾、权限不对、或误当 MySQL 用。CI4 本身对 SQLite 支持良好,关键是尊重它的文件数据库本质。











