SQLite 开启 WAL 模式提高并发读写能力
wxk1991 Lv5

SQLite 开启 WAL 模式提高并发读写能力

SQLite 默认就很好用。

但如果你的程序有较多并发读写,就应该了解:

1
WAL 模式

WAL 全称:

1
Write-Ahead Logging

简单理解:

1
写入先进入 WAL 日志文件,读请求仍然可以继续读取旧的数据快照。

这样可以减少读写互相阻塞的情况。

SQLite WAL 并发读写架构


一、SQLite 默认模式的问题

SQLite 默认 journal 模式通常是:

1
DELETE

在这种模式下,写事务可能会阻塞读事务。

如果你的程序是:

  • 单用户脚本
  • 小工具
  • 偶尔写入
  • 主要读数据

默认模式够用。

但如果是:

  • Web API
  • 多线程读写
  • 后台任务持续写入
  • 前端频繁查询
  • 日志/任务表高频更新

就可以考虑开启 WAL。


二、开启 WAL 模式

进入 SQLite:

1
sqlite3 app.db

执行:

1
PRAGMA journal_mode=WAL;

查看当前模式:

1
PRAGMA journal_mode;

如果返回:

1
wal

说明已经开启。

也可以直接命令行执行:

1
sqlite3 app.db "PRAGMA journal_mode=WAL;"

三、设置 busy_timeout

WAL 不是让 SQLite 变成无限并发数据库。

如果多个写操作同时发生,仍然可能竞争写锁。

建议设置:

1
PRAGMA busy_timeout=5000;

意思是:

1
遇到锁等待时,最多等 5000 毫秒。

命令行:

1
sqlite3 app.db "PRAGMA busy_timeout=5000;"

在 Python 里:

1
2
3
4
5
import sqlite3

conn = sqlite3.connect("app.db", timeout=5)
conn.execute("PRAGMA journal_mode=WAL;")
conn.execute("PRAGMA busy_timeout=5000;")

四、设置 synchronous

常见搭配:

1
PRAGMA synchronous=NORMAL;

完整初始化:

1
2
3
PRAGMA journal_mode=WAL;
PRAGMA synchronous=NORMAL;
PRAGMA busy_timeout=5000;

说明:

  • FULL:更保守,安全性更高
  • NORMAL:WAL 下常用,性能更好
  • OFF:不建议生产使用

一般 Web 小项目可以用:

1
WAL + synchronous=NORMAL + busy_timeout

五、Node.js 中开启 WAL

如果你使用 better-sqlite3

1
pnpm add better-sqlite3

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import Database from 'better-sqlite3'

const db = new Database('app.db')

db.pragma('journal_mode = WAL')
db.pragma('synchronous = NORMAL')
db.pragma('busy_timeout = 5000')

db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL
)
`)

六、Python 中开启 WAL

Python 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import sqlite3


def connect_db():
conn = sqlite3.connect(
"app.db",
timeout=5,
isolation_level=None,
check_same_thread=False,
)
conn.execute("PRAGMA journal_mode=WAL;")
conn.execute("PRAGMA synchronous=NORMAL;")
conn.execute("PRAGMA busy_timeout=5000;")
return conn

注意:

1
check_same_thread=False 不是让连接天然线程安全。

它只是允许连接跨线程使用。

真实项目里,更推荐:

1
每个线程/请求使用独立连接

七、WAL 会生成哪些文件

开启 WAL 后,你会看到:

1
2
3
app.db
app.db-wal
app.db-shm

含义:

文件 作用
app.db 主数据库
app.db-wal 写前日志
app.db-shm 共享内存索引

不要随便只复制 app.db

如果数据库正在运行,最好先 checkpoint 或停服务后备份。


八、手动 checkpoint

WAL 文件可能会变大。

可以执行:

1
PRAGMA wal_checkpoint(TRUNCATE);

命令行:

1
sqlite3 app.db "PRAGMA wal_checkpoint(TRUNCATE);"

这会尝试把 WAL 里的内容写回主库,并截断 WAL 文件。


九、什么时候不适合 WAL

WAL 不是万能的。

不适合:

  • 数据库放在不可靠网络文件系统
  • 写入并发极高
  • 多进程疯狂写
  • 需要复杂事务隔离
  • 数据规模持续扩大到超出 SQLite 适合范围

如果你已经有大量并发写入,应该考虑:

1
2
PostgreSQL
MySQL

而不是硬扛 SQLite。


十、推荐配置

小型 Web 项目推荐:

1
2
3
4
PRAGMA journal_mode=WAL;
PRAGMA synchronous=NORMAL;
PRAGMA busy_timeout=5000;
PRAGMA foreign_keys=ON;

如果使用 Node.js:

1
2
3
4
db.pragma('journal_mode = WAL')
db.pragma('synchronous = NORMAL')
db.pragma('busy_timeout = 5000')
db.pragma('foreign_keys = ON')

如果使用 Python:

1
2
3
4
conn.execute("PRAGMA journal_mode=WAL;")
conn.execute("PRAGMA synchronous=NORMAL;")
conn.execute("PRAGMA busy_timeout=5000;")
conn.execute("PRAGMA foreign_keys=ON;")

总结

SQLite 开启 WAL 后,最大的收益是:

1
读写并发体验更好。

它不会让 SQLite 变成大型数据库。

但对于个人项目、小后台、轻量 API、本地工具来说,WAL 非常值得开启。

推荐记住这一组:

1
2
3
4
PRAGMA journal_mode=WAL;
PRAGMA synchronous=NORMAL;
PRAGMA busy_timeout=5000;
PRAGMA foreign_keys=ON;