用 SQLite 存日志踩到的三个坑
这个站的访问日志和几个小工具的运行记录,我图省事都塞进了一张 SQLite 表。数据量不大,一天几千行,按理说不会有事。结果一周里连着碰到三个问题,记一下。
一、WAL 文件一直长
开了 WAL 模式之后,主库文件不大,但 -wal 文件能涨到几百兆。原因是读连接一直开着,checkpoint 没法把变更合回主库。后来加了个定时任务,每小时跑一次:
PRAGMA wal_checkpoint(TRUNCATE);
注意这条在有长事务的时候会返回忙,别当成失败重试就好。
二、删了半年数据,空间没还回来
按天清理旧行之后,表里只剩几万行,文件还是原来那么大。SQLite 删除只是把页标成空闲,不会自动收缩。要么定期 VACUUM,要么建表时就设 auto_vacuum(后者要在空库上设,改已有库得先整理一遍)。
三、并发写偶尔报 SQLITE_BUSY
两个写入方同时跑的时候,偶尔会直接抛错。默认等待时间是 0,谁先拿到锁谁写,另一个立刻失败。两行就解决了:
PRAGMA busy_timeout = 5000;
PRAGMA journal_mode = WAL;
设置完还得确认每个连接都执行了——这类连接级参数不会跟着数据库文件走。
三个问题都不复杂,但都不报错,只是安静地把磁盘占满、或者偶尔丢一条记录。以后再用 SQLite 存日志,我大概会先把这几条写进建库脚本里。