概述
SQLite 有个名声:适合单机、不适合并发。很多人用它做本地数据库、小项目存储,一但并发上来的就卡死。
问题出在默认模式上。SQLite 默认用 DB 模式(write-ahead logging 关闭),写操作会锁整张表,读操作也要排队等。一个写入,所有读取阻塞。
开 WAL 模式就解决了这个问题。WAL(Write-Ahead Logging)让读写并行——写操作不阻塞读,多个读可以同时跑。性能提升非常明显,尤其是读多写少的场景,比如 AI 知识库、日志存储、应用配置。
开启 WAL 只需要一条 SQL,5 分钟搞定,不改变任何数据结构。
准备工作
你需要以下条件:
- 一台已安装 SQLite 的机器(Ubuntu / macOS / 任意 Linux 发行版)
- SQLite 3.7.0+(2009 年发布,基本都满足)
- 你的应用代码(Python / Node.js / Go 均可)
- 约 0 额外磁盘空间(WAL 文件只在大负载时增长)
检查当前模式:
sqlite3 --version
sqlite3 mydb.sqlite "PRAGMA journal_mode;"
返回 DELETE 就是默认 DB 模式,需要开启 WAL。
步骤
第一步:直接开启 WAL 模式
sqlite3 mydb.sqlite "PRAGMA journal_mode=WAL;"
执行完立刻生效,不需要重启,不需要迁移数据。
返回 WAL 就说明成功了。
第二步:创建 Python 脚本做自动化配置
如果你用 Python 管理数据库,把 WAL 模式写进初始化脚本里:
import sqlite3
def init_db(db_path: str = "app.db"):
conn = sqlite3.connect(db_path)
cursor = conn.cursor()
# 开启 WAL 模式
cursor.execute("PRAGMA journal_mode=WAL;")
print(f"Journal mode: {cursor.fetchone()[0]}")
# 推荐同时开启的优化参数
cursor.execute("PRAGMA synchronous=NORMAL;") # 减少磁盘刷盘次数,提升写入性能
cursor.execute("PRAGMA cache_size=-64000;") # 64MB 缓存
cursor.execute("PRAGMA temp_store=MEMORY;") # 临时表放内存
conn.commit()
conn.close()
print("SQLite WAL + 优化参数已生效")
if __name__ == "__main__":
init_db()
第三步:Node.js 项目中的配置
const sqlite3 = require('sqlite3').verbose();
const db = new sqlite3.Database('./app.db');
db.run(`
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA cache_size = -64000;
`, (err) => {
if (err) console.error('初始化失败:', err);
else console.log('SQLite WAL 模式已开启');
});
第四步:Go 项目中的配置
import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
func initDB() *sql.DB {
db, err := sql.Open("sqlite3", "./app.db?_journal_mode=WAL&_synchronous=NORMAL")
if err != nil {
panic(err)
}
return db
}
连接 URL 里直接加参数,比执行 PRAGMA 更简洁。
第五步:验证 WAL 模式生效
# 方法1:直接查询
sqlite3 mydb.sqlite "PRAGMA journal_mode;"
# 返回 WAL
# 方法2:查看目录下文件
ls -la mydb.sqlite*
# 正常情况下会看到三个文件:
# mydb.sqlite - 主数据库文件
# mydb.sqlite-wal - WAL 日志文件
# mydb.sqlite-shm - 共享内存文件
有 -wal 和 -shm 两个文件就说明 WAL 模式已启用。
验证
# 完整验证流程
cd ~/myproject
# 1. 检查当前模式
sqlite3 mydb.sqlite "PRAGMA journal_mode;"
# 期望输出: WAL
# 2. 测试并发读写性能
python3 -c "
import sqlite3, threading, time
conn = sqlite3.connect('mydb.sqlite')
conn.execute('PRAGMA journal_mode=WAL')
conn.execute('CREATE TABLE IF NOT EXISTS test(id INTEGER PRIMARY KEY, val TEXT)')
conn.execute('INSERT OR IGNORE INTO test VALUES(1, \"hello\")')
conn.commit()
results = []
def writer():
for i in range(1000):
conn.execute(f\"INSERT INTO test(val) VALUES('write-{i}')\")
conn.commit()
def reader():
for i in range(1000):
rows = conn.execute('SELECT * FROM test').fetchall()
results.append(len(rows))
t1 = threading.Thread(target=writer)
t2 = threading.Thread(target=reader)
t1.start(); t2.start()
t1.join(); t2.join()
print(f'读取成功次数: {sum(1 for r in results if r > 0)}/{len(results)}')
print(f'总记录数: {conn.execute(\"SELECT COUNT(*) FROM test\").fetchone()[0]}')
"
# 3. 查看 WAL 文件大小
ls -lh mydb.sqlite-wal
WAL 模式下,读写可以同时跑,不会出现读被写阻塞的情况。
常见问题
Q:WAL 模式会影响数据安全吗?
不会。WAL 模式同样保证事务完整性,崩溃恢复机制和 DB 模式一样可靠。唯一的区别是写入性能更好,不会锁表。
Q:WAL 文件会一直增长吗?
不会。WAL 文件有自动 checkpoint 机制,当所有已提交的改动都刷入主数据库文件后,WAL 文件会被截断。你可以手动触发 checkpoint:
sqlite3 mydb.sqlite "PRAGMA wal_checkpoint(TRUNCATE);"
Q:SQLite WAL 模式有什么限制?
- 不支持
LOCKING=EXCLUSIVE的写操作(极少用到) - 文件数量多一个(-wal 和 -shm),备份时需要一起处理
- 极端高并发写入场景下,checkpoint 可能成为瓶颈,需要调大
cache_size
Q:已经有大量数据的数据库能直接开启 WAL 吗?
可以。直接在现有数据库上执行 PRAGMA journal_mode=WAL; 就行,数据不会丢失,现有连接不受影响。
Q:WAL 模式对写入性能提升大吗?
中等规模写入(每秒几百次)能提升 2-5 倍。大量并发写入(每秒上千次)提升更明显,但要注意 checkpoint 频率,太频繁会抵消部分收益。
总结
一行 SQL 开启 WAL,读写并行,性能暴涨。SQLite 不是不能并发,只是默认配置不够积极。
你的 SQLite 数据库现在是什么模式?跑一下 PRAGMA journal_mode; 看看。