SQLite WAL 模式开启:让并发读取快 10 倍的简单配置

概述

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; 看看。

这篇文章对你有帮助吗?