公众号旧文
删除日志时,为什么既不是按行也不是按大小
我刚执行了:
journalctl --vacuum-size=50M
随后马上产生了一个疑问:
- 是不是按日志行数删除?
- 是不是按字节数删除?
如果是按行数删除,那么剩余日志大小,肯定不是正好的 50M。
如果是按字节数删除,保留最近的50M,那么日志肯定会被截断。
实际上,systemd-journald 的工作方式和传统文本日志完全不同。
一、很多人会误以为日志是文本
例如 /var/log/syslog、/var/log/messages 这样的日志文件,本质上就是文本:
Jul 12 10:00:01 kernel: ...
Jul 12 10:00:02 sshd: ...
Jul 12 10:00:03 systemd: ...
如果是文本文件,那么理论上可以:
- 删除前 1000 行
- 删除前 10MB
- 删除指定时间以前的内容
因为文本可以从任意位置截断。
很多日志管理工具(例如 logrotate)就是这样工作的。
二、但 journald 根本不是文本
systemd-journald 保存的是一种专门设计的二进制格式。
例如:
/var/log/journal/
└── 9f7b2d9c1d8f4f3d8b0d...
├── system@0000000000000001.journal
├── system@0000000000000002.journal
├── system@0000000000000003.journal
└── system.journal
这里每一个 .journal 文件都是一个完整的数据文件,而不是普通文本。
这些文件内部不仅保存日志内容,还包含:
- 索引(Index)
- 时间戳
- 元数据(Metadata)
- 校验信息(Checksum)
- 对象引用(Object)
- 哈希表(Hash Table)
因此,它不能像文本一样随意删除中间的一部分。
三、Vacuum 的删除单位是什么?
答案是:
整个 Journal 文件。
例如当前有四个日志文件:
| 文件 | 大小 |
|---|---|
| A.journal | 20MB |
| B.journal | 18MB |
| C.journal | 25MB |
| D.journal | 57MB |
总大小:
120MB
现在执行:
journalctl --vacuum-size=50M
可能有些人会认为系统将:
删掉 A 的一半
删掉 B 的三分之一
实际上并不会。
真正删除的是:
整个 A.journal
如果仍然超过限制:
再继续删除整个 B.journal
直到满足限制条件。
因此,删除单位始终是整个 Journal 文件。
四、为什么不会精确等于 50MB?
这是因为 Journald 存在两种日志文件。
Archived Journal(归档日志)
已经关闭,不再写入。
这些文件可以直接删除。
例如:
system@0001.journal
system@0002.journal
system@0003.journal
Active Journal(活动日志)
当前仍然在写入。
例如:
system.journal
这个文件正在不断追加新的日志。
它不能删除,更不能从中间截断。
否则整个 Journal 文件都会损坏。
因此,Journald 永远不会:
- 删除活动文件的一半
- 删除活动文件前 10MB
- 删除活动文件中的部分记录
五、一个实际例子
假设当前目录如下:
| 文件 | 大小 |
|---|---|
| A | 20MB |
| B | 18MB |
| C | 25MB |
| D(当前正在写) | 57MB |
总大小:
120MB
执行:
journalctl --vacuum-size=50M
删除过程可能是:
删除 A
剩余 100MB
删除 B
剩余 82MB
删除 C
剩余 57MB
此时虽然仍然超过 50MB,
但是:
57MB
已经全部来自当前活动日志文件。
因为它不能被拆开,所以 Journald 会停止删除。
因此最终结果可能就是:
57MB
而不是:
50MB
这是完全正常的。
六、为什么不能按 MB 精确删除?
有人会问:
既然知道目标是 50MB,为什么不能把最后一个文件删掉 7MB?
原因很简单:
.journal 文件并不是一个连续的文本。
它内部由大量对象组成:
Header
Object
Object
Hash Table
Index
Data
Entry
Field
...
所有数据之间都有索引关系。
如果随意删除中间一部分:
比如删掉前 7MB
那么:
- 索引全部失效
- 哈希损坏
- 校验失败
- 整个 Journal 文件无法读取
因此,Journald 从设计上就不支持这种操作。
七、SystemMaxUse 真正的含义
可能会有人把:
SystemMaxUse=50M
理解成日志一定保持 50MB。
实际上更准确的理解应该是:
尽量将 Journal 总大小控制在约 50MB 附近。
它并不是一个绝对值。
实际大小可能是:
48MB
49MB
50MB
53MB
57MB
都属于正常现象。
因为 Journald 始终优先保证日志文件的完整性,而不是追求精确的数字。