日志存储基础:传统存储模式与存储格式

聊审计日志存储,第一个绕不开的话题就是——存在哪?怎么存?

我见过不少团队,一开始图省事,随便往文件里一写,等日志量起来了,查一条记录要翻半天。也有的直接用数据库表硬扛,结果写入压力把业务库拖垮了。说白了,存储选型决定了后续索引能不能建得上去。

这一节我把传统存储模式和常见格式掰开了讲。先不讲分布式方案,那些后面有专门章节(嗯,但你要知道,很多分布式方案底层绕不开这俩基础模式)。

存储模式:文件还是数据库?

传统日志存储就两条路:文件存储和数据库存储。我习惯用下面这个表来对比,每次选型都拿出来看一眼。

维度 文件存储 数据库存储
写入方式 追加写(append only),几乎无锁 行插入,事务、索引维护有额外开销
读取查询 需要线性扫描或二分查找(带索引文件) SQL 查询,支持条件过滤、聚合、排序
索引能力 需要自己实现索引(如倒排、B+树文件) 内置 B+ 树 / 哈希索引,但建索引有写放大
一致性保证 靠文件系统同步,断电可能丢最后几条 ACID 事务,但高并发下性能衰减明显
扩展性 分文件(按时间、按用户),天然水平 分表、读写分离、分库,复杂度较高
典型场景 高吞吐、低价值、保留期短的审计日志 需要实时检索、复杂查询的审计日志

我的经验: 文件存储更适合流水线式的日志落盘,比如 Nginx 访问日志、系统 syslog。但一旦你需要按某个字段频繁检索,比如“查某个人最近10条操作”,文件模式没有索引就非常痛苦。

我记得有一次做金融审计项目,把日志全写进了 PostgreSQL 的普通表,每天几亿条插入,磁盘 I/O 直接打满。后来改成先写文件(按小时分片)再异步批量导入数据库,才把问题解决掉。

存储格式:文本、二进制、JSON

格式选择直接影响存储效率和解析速度。我按我最常用的三个方向来说。

文本格式(行协议)

典型代表:Apache 的 common log format、自定义“key=value”格式。每行一条记录,字段用分隔符(空格、逗号、竖线)隔开。

2025-03-20 10:15:30 | user_10086 | OPERATION | DELETE | /api/orders/98765 | SUCCESS

优点:人类可读,用 `grep`、`awk` 就能秒级排查。缺点也很明显:解析效率低(每行都要拆分字符串),存储冗余大(字段名重复出现)。

我曾经用文本格式存过亿级日志,单行平均200字节,压缩后还有40字节。查询时用 `grep` 配合索引文件(提前按时间建好偏移表)勉强能用,但跨天查询就慢了。

二进制格式

典型代表:protobuf、Thrift、自研结构体序列化。每条记录按固定 schema 编码,字段按类型编码。

// protobuf 消息定义
message AuditLog {
  int64  timestamp = 1;
  string user_id = 2;
  int32  op_type = 3;
  string resource = 4;
  bytes  detail = 5;
}

优点:体积小(可压缩至文本的 1/3~1/5),解析快(按偏移直接取字段)。缺点:肉眼无法直接看,需要用工具转码才能调试。

温馨提示: 如果团队监控、排查主要靠脚本和日志文件头,二进制会大幅拉高排障成本。我建议审计日志先用文本格式快速迭代,稳定后再切换到二进制以求存储效率。

JSON 格式

不用多解释,现在大部分应用框架默认输出 JSON。每条日志是一个 JSON 对象。

{
  "time": "2025-03-20T10:15:30Z",
  "user": "user_10086",
  "action": "DELETE",
  "resource": "/api/orders/98765",
  "result": "SUCCESS"
}

JSON 的优点在于灵活——schema 可以随时加字段。但膨胀率惊人:同样的数据,JSON 比二进制大 2~4 倍,而且解析需要做 JSON parse,比文本行解析还慢。

我在项目中遇到过极端情况:某个服务改了一版接口,把日志里的 `resource` 字段从字符串改成了嵌套对象,导致下游日志解析全部挂了。JSON 的自由是一把双刃剑。

格式对比汇总

格式 存储体积(相对) 解析速度 人类可读 Schema 灵活性 推荐场景
文本行 大 慢 高 低 开发调试、小规模日志
二进制 小 快 低(需工具) 低(Schema 固定) 高吞吐、长期存储、性能敏感
JSON 很大 慢(解析成本高) 中(可格式化查看) 高 多团队协作、快速迭代

避坑指南: 不要在生产环境直接用 JSON 做百万级日志的实时检索,否则 CPU 和内存成本会让你怀疑人生。我曾经犯过这个错——上线第一天就把日志存储节点的 GC 时间打到了 30% 以上。

传统存储的整体架构图

下面这张图梳理了文件与数据库两种模式,以及各自适用的格式和索引构建方式。你后续学索引优化时,会发现几乎所有优化手段都从这里出发。

传统日志存储知识体系图 传统日志存储知识体系 存储模式 文件存储(Append Only) 数据库存储(关系型) 文本格式 / 二进制 / JSON 一般用 JSON 或二进制 BLOB 索引方式:文件偏移表 / 倒排索引 索引方式:B+ 树 / 哈希索引 选择标准:吞吐量 vs 查询复杂度 vs 存储成本 小规模用文件+文本,大规模可考虑混合架构 索引优化从这里起步

你会发现,不管是文件还是数据库,下一步都要建索引。但建索引的方式完全不同——文件模式索引是自己写的“外挂”,数据库模式索引是内置的。后面的章节会深入这两种索引的实现细节。

嗯,这一节内容比较多,但都是基础。你把这几个概念理清了,后面讲索引优化时才不会迷糊。我建议你回头看看那个对比表格,尤其是“文件 vs 数据库”和“格式对比”,平时选型直接照搬就行。