个人量化程序的数据量通常不算大,用 SQLite 保存行情、信号和回测结果很方便,不需要单独维护数据库服务。不过,当程序从单脚本拆成行情、策略和交易几个进程后,偶尔会遇到 `database is locked`。很多人第一反应是开启 WAL,但 WAL 只能改善读写并发,并不能让多个进程随意同时写入。
WAL 模式下,读取和写入可以同时进行,但同一时间仍然只有一个写入者。如果一个进程开启事务后又去请求行情接口,写锁会一直被占着,其他进程只能等待。
比较稳妥的做法是:网络请求和数据计算放在事务外,事务里只执行必要的 SQL,并尽快提交。
python
import sqlite3
conn = sqlite3.connect("quant.db", timeout=5)
conn.execute("PRAGMA journal_mode=WAL")
conn.execute("PRAGMA busy_timeout=3000")
# 先在事务外完成接口请求和指标计算
rows = [("000001.SZ", "2026-09-28", 10.52)]
with conn:
conn.executemany(
"INSERT OR REPLACE INTO daily_bar(symbol, trade_date, close) "
"VALUES (?, ?, ?)",
rows,
)
`busy_timeout` 可以缓解几百毫秒的短暂竞争,但解决不了长事务。如果锁冲突频繁,更合适的结构是让多个进程负责计算,统一把写入任务交给一个进程处理。
WAL 模式运行时,尚未合并的内容可能还在 `quant.db-wal` 中。程序运行期间只复制 `quant.db`,得到的备份可能缺少最近的数据。Python 可以直接使用 SQLite Backup API:
python
source = sqlite3.connect("quant.db")
target = sqlite3.connect("quant-backup.db")
source.backup(target)
target.close()
source.close()
备份完成后,建议实际打开备份库,查询最新交易日和关键表行数。能成功复制不等于能正常恢复。
SQLite 默认会自动执行检查点,把 WAL 中的内容合并回主数据库。如果某个读取任务长时间不结束,例如遍历多年分钟线却一直保持同一个事务,检查点可能无法完成,WAL 文件就会持续增长。
因此,长时间分析任务最好分批读取并及时结束事务,同时监控 `-wal` 文件大小。WAL 还依赖共享内存文件,只适合在同一台机器上使用,不应把数据库放到网络共享目录后让多台服务器共同读写。
对单机、小规模量化任务来说,SQLite WAL 仍然很实用。关键不是多加几次重试,而是缩短写事务、集中写入、正确备份,并避免长时间占用读取事务。超过这些边界后,再考虑迁移到服务端数据库会更省心。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。