背景
我们公司有大量的 PHP 和 Shell 脚本需要定时执行。起初使用 Linux 的 crontab
管理任务,但随着业务的增长,定时任务数量一度超过 50 个,管理成本迅速上升。
为了方便线上操作,当时在 GitHub 上找到了 lisijie/webcron 这个项目。它功能简单,能满足基本的定时任务管理需求,我们便直接投入使用。
遇到的问题
随着时间推移,我们开始遇到以下两个关键问题:
- 日志查询影响主业务性能
Webcron 默认使用 MySQL 存储任务和日志,与业务共用数据库。当日志量激增后,任务列表页面需要联表查询日志,页面加载时间常超过 30 秒,严重影响了主业务性能。 日志数据膨胀,存储压力大
- 部分任务执行频率高(如每 10 秒一次);
- 部分任务每次输出大量日志(3MB+);
- 日志需保留 6 个月。
例如:
项目 | 月记录数 | MySQL .ibd 文件体积 |
---|---|---|
A 项目 | 120 万条 | 1.2 GB |
B 项目 | 480 万条 | 8.9 GB |
如果按照此增长速度保留半年,B 项目的日志将占用约 50GB 的磁盘空间。
解决方案
我们决定重构整个定时任务管理系统,重点优化三方面:
1. 用 SQLite 替代 MySQL 存储日志
MySQL 查询开销大、影响业务,我们将日志存储迁移到 SQLite。它无需额外服务,具备完整 SQL 支持,适合做独立日志存储方案,查询性能也更可控。
2. 使用 zstd 压缩日志内容
观察日志内容后发现,大量输出存在重复格式,非常适合压缩。我们评估了几种压缩算法:
zstd
(默认级别 / L7级别)zstd + 自训练字典
gzip
(Go 标准库实现)
压缩对比如下:
压缩方案 | A 项目(120 万) | B 项目(480 万) |
---|---|---|
MariaDB .ibd 文件 | 1.2 GB | 8.9 GB |
SQLite 明文存储 | 856 MB | 665 MB |
gzip(默认) | 212 MB | 405 MB |
zstd(无字典) | 198 MB | 335 MB |
zstd + B 项目字典 | 149 MB | 192 MB |
zstd L7 + B 项目字典 | 139 MB | 186 MB |
zstd + A 项目字典 | 95.1 MB | 224 MB |
注:
- 字典通过
zstd --train
命令生成,使用项目历史日志训练;- 字典在不同项目中交叉使用效果不一;
- 字典需维护更新,综合考虑后未在最终方案中使用。
最终策略是:当单条日志长度 > 150 字节时启用 zstd 压缩,否则直接存储明文。读取时自动判断并解压。
3. 分月存储日志文件
为了避免单文件过大并简化清理流程,我们将日志按月分别存储,每月 1 号新建一个 SQLite 日志库文件(如 2025-08.db
)。
这样带来两个好处:
- 查询范围控制在单月文件,性能更稳定;
- 清理历史数据只需删除
.db
文件即可,维护成本极低。
实际效果
经过重构后,系统在实际运行中表现稳定,存储与性能均有显著改善:
- 压缩后磁盘占用:每 10 万条日志占用约 9~10 MB;
- 查询性能:500 万条日志文件中,查询指定任务最近 20 条记录耗时 < 20ms;
- 支持字段:支持根据任务 ID、执行状态、执行时间进行高效查询。
技术实现细节
- 开发语言:Go 1.24
- 数据库引擎:GORM + SQLite(无 cgo 版本)
- 压缩库:github.com/klauspost/compress
为什么不用列式压缩?
日志是按行追加写入的,若采用列式压缩,需要额外拆分压缩、整合再写入逻辑,开发成本较高,不适合高频日志场景。因此我们采用了“逐行压缩”策略,开发上更轻量。
灵感来源与参考
本项目思路部分参考了 V2EX 上的帖子:使用 SQLite 存日志 + zstd 压缩,但实际测试中压缩比与原贴略有差异。总体来说,该方案在中小型系统中表现优异,值得借鉴。
后续优化方向
- ✅ 探索 DuckDB 替代 SQLite:支持列式存储,可能带来更高压缩比与查询性能;
- ✅ 动态字典优化:尝试用上月日志训练下月字典,实现自动演进,提高长期压缩效率;
- ✅ 分布式合并查询支持:支持跨月日志合并检索,便于追溯问题全链路。
小结
这次定时任务系统重构,核心目标是“日志系统轻量化、可维护、低资源占用”。通过 SQLite + zstd 压缩 + 分月归档的组合,我们在不依赖额外服务的前提下,成功将海量日志压缩到可控范围,并提升了查询体验。
如果你的系统也面临日志存储冗余、查询卡顿等问题,或许可以尝试这种“小数据库 + 压缩”的路线。
文章内容由两双筷子原创,使用 ChatGPT 进行排版优化。