许多人对 SQLite 存在误解,认为“每次写入都会锁定整个数据库”。但实际上,只需一个简单的配置,就能让它轻松应对数千并发用户——这就是Write-Ahead Logging (WAL) 模式。
-- 启用WAL模式
PRAGMA journal_mode=WAL;
-- 设置同步模式为NORMAL(性能与安全性的平衡)
PRAGMA synchronous=NORMAL;
WAL 原理
WAL 是一种日志机制,它将数据修改先写入单独的日志文件,再定期同步到主数据库。这种设计带来了两大核心优势:
- 读写并发:读不再阻塞写,写也不再阻塞读
- 崩溃恢复:即使系统崩溃,也能从日志中恢复未提交的数据
流程:
-
写入流程:
- 数据修改先写入
-wal日志文件 - 主数据库文件保持不变
- 定期将日志内容批量写入主文件
- 数据修改先写入
-
读取流程:
- 读者从主数据库文件读取
- 同时检查WAL日志中的新数据
- 获得数据的一致性视图
-
检查点机制:
- 定期将WAL日志内容合并到主文件
- 保持日志文件大小可控
- 可通过
PRAGMA wal_checkpoint手动触发
注意事项
注意:不支持分布式部署、不能高频操作的问题依旧还在,WAL 不能解决所有问题。
- 网络文件系统:WAL在NFS等网络文件系统上可能有问题
- 多进程访问:多个进程可同时读取,但写入仍需协调
- 备份策略:备份时需要同时复制
.db和-wal文件 - 内存使用:WAL会占用更多内存缓存日志内容
附录
1. PRAGMA journal_mode
此指令用于设置数据库的日志模式,它决定了事务如何被记录和回滚。其可选值为:
| 值 | 说明 |
|---|---|
| DELETE | 默认模式。在事务结束时,回滚日志文件会被删除。这是 SQLite 最传统的日志模式。 |
| TRUNCATE | 与 DELETE 类似,但不实际删除文件,而是将日志文件截断为0字节。在某些系统上可能比 DELETE 更快。 |
| PERSIST | 不回滚日志文件置零或删除,而是在文件头写入一个无效的魔数,使日志文件不被视为有效。这可以避免删除操作,在部分文件系统上可能提升速度。 |
| MEMORY | 将回滚日志存储在内存中而非磁盘上。这能提升事务速度,但一旦程序崩溃或断电,整个数据库可能损坏。仅用于临时数据库。 |
| WAL | Write-Ahead Logging 模式(即本文作者采用的模式)。修改先写入一个单独的 WAL 文件,支持读写并发。提供了最好的并发性能,是多数现代应用的推荐选择。 |
| OFF | 完全禁用回滚日志。事务通过独占数据库锁来实现,无法回滚。性能最高,但安全性最低,不推荐常规使用。 |
2. PRAGMA synchronous
此指令用于控制 SQLite 在将数据写入磁盘时的“同步”严格程度,即在何时向操作系统确认写入操作已完成。它直接影响数据安全性与写入性能的权衡。其可选值为:
| 值 | 说明 |
|---|---|
| OFF (0) | 安全性最低,性能最高。SQLite 将数据交给操作系统后立即继续,不等待实际写入磁盘。如果系统在之后崩溃,数据可能损坏。 |
| NORMAL (1) | 性能与安全性的平衡(即本文作者采用的模式)。在大多数关键操作后执行同步,但不像 FULL 那样频繁。在非系统崩溃(如普通程序崩溃)时能保证数据安全,是 WAL 模式下的常用搭配。 |
| FULL (2) | 安全性最高,性能较低(是除 WAL 模式外的默认值)。确保所有数据在继续前都已物理写入磁盘。这是最安全的模式,只要磁盘本身未损坏,数据就不会损坏。 |
| EXTRA (3) | 比 FULL 更严格,仅在极少需要额外耐久性保证的场景下使用。 |