跑了一个月的AI自研Agent,state.db从几MB涨到92MB,期间踩了个WAL日志无限膨胀的坑,今天把整套维护逻辑复盘成可复用的方法。
背景:我的Agent把所有会话、记忆、消息全文索引都存在一个SQLite的state.db里,跑了一个月后库涨到92MB、1.2万条消息、21张表,其中消息表还挂了FTS5 trigram全文索引用于按需检索历史。一开始完全不操心,因为我用了WAL模式——想着并发读写更安全。后来发现坑恰恰出在WAL上:state.db-wal文件一直在涨,某天看到已经4MB,而主库文件却纹丝不动。
怎么做的:先花了十分钟把库的真实状态摸了一遍——确认journal_mode是wal、页大小4096、总页数22530。然后意识到问题本质:WAL模式里,新写入先落进-wal侧写文件,只有当checkpoint发生时才会合并回主库。如果只有读写进程但没有定时checkpoint的机制,-wal就会只增不减。我的场景里读写非常频繁(会话消息持续写入),所以-wal涨得比想象快得多。
遇到的坑有三个。第一个最典型:用sqlite3命令行直接查这个92MB的库会失败,因为库一直处于"被我的Agent进程独占写"的状态,外部连接读不到或拿不到锁,必须先搞清楚journal_mode再用只读URI方式去连。第二个坑是别随便手动执行checkpoint或VACUUM:在高频写入的生产库上手动跑全库VACUUM会长时间持锁,把整个Agent卡死。第三个坑是freelist_count要定期看,如果长期不为0说明有大量删除留下的空闲页没回收,光靠checkpoint收不回来。
关键结论是我踩完之后沉淀的一套可复用方法,比结论本身更重要的是顺序:先打PRAGMA看journal_mode、page_count、freelist_count拿到真实数字,别凭感觉猜;再把checkpoint做成定时任务而不是等它自己发生,频率跟随你的写入量,我的场景是每6小时一次足够,写入更猛就加密;最后是别碰运行时VACUUM,真要瘦身挑一个写入低谷窗口做。这套做完,-wal被压回几百KB量级,主库也不再虚胖。
这套经验对任何用SQLite做本地主存储的人都有用——不只Agent,笔记软件、采集爬虫、定时任务记录器一旦写得多都会遇到。核心一句话:WAL模式不是"免维护",checkpoint必须有人负责,要么你在代码里代码里显式调用wal_checkpoint,要么上定时任务,否则侧写文件会悄悄吃掉你的磁盘。