概述
当主机或内存 cgroup 耗尽内存时,Linux OOM (内存不足) killer 会使用 SIGKILL 终止一个进程——通常是占用内存最多的那个;而在专用主机上,这往往就是 clickhouse-server 本身。这样一来,整个服务器会直接丢失,而不是获得恢复的机会。
OOM canary 改变了“谁先死”。它会运行一个小型的牺牲性子进程,使其成为最容易被 OOM 选中的目标,这样内核杀掉的就是它,而不是服务器。随后,服务器会检测到该进程已死亡,确认这是一次 OOM 事件,并缓解内存压力,从而让自己存活下来。
canary 不会提高任何 memory limit,也不能替代正确设置的 limits (参见 内存 overcommit 和 max_server_memory_usage) 。它是最后一道防线:用少量固定的内存,换取在内存突增时存活下来的机会。
工作原理
canary 是一个独立的 clickhouse oom-canary 进程。它会将自身的
oom_score_adj 设为最大值 (1000) ,让内核优先将其作为目标;随后分配、触碰并对
oom_canary_size 字节执行 mlock (默认 100 MB) ,以确保其常驻内存集真实存在。若
server 退出,它也会被自动终止。
在 server 中,一个监控线程会通过 pidfd 监视 canary,并在
它死亡时作出响应:
- 因
SIGKILL被杀死,且存在 cgroup OOM 证据 → 执行 OOM 响应,然后 重新启动一个新的 canary。 - 被杀死但没有 OOM 证据 (例如手动执行
kill -9) ,或者因暂时性故障退出 → 仅重新启动,不执行响应。 - 永久性设置失败,或 server 关闭 → canary 会自行禁用。
OOM 证据仅来自 cgroup v2 memory.events.local 中的 oom_kill
计数器。这特意限定为 cgroup 本地:分层计数器或主机级计数器可能会因不相关进程而递增,
从而触发误响应。
确认发生 OOM 后,会执行以下彼此独立的响应步骤:记录一条 FATAL
消息,清理 allocator (jemalloc) 的 arenas,尽最大努力取消所有正在运行的
查询,取消所有合并和变更,并在
system.crash_log 中排入一个事件。系统日志不会同步刷新,
因为在内存压力下强制执行 I/O 可能会让情况变得更糟。
要求
- Linux ≥ 5.3。 监控器通过
pidfd_open持有 canary;在较旧的内核上, canary 会在启动时自行禁用。在非 Linux 平台上,它是空操作。 - 用于 OOM 响应且带有
memory.events.local的 cgroup v2。 如果没有它, canary 在收到SIGKILL后仍会重新启动,但无法确认是否发生了 OOM,因此 响应永远不会执行 (启动时会记录一条警告) 。 mlock能力 (可选) 。 锁定 canary 的内存需要CAP_IPC_LOCK或足够的RLIMIT_MEMLOCK;如果失败,canary 会记录一条 警告,并且其内存可能被换出,从而削弱其作为 OOM 目标的作用。
配置
canary 由服务器设置控制, 作为服务器配置的顶层元素进行设置,并在重启后生效。
| Setting | Default | Description |
|---|---|---|
oom_canary_enable |
false |
启用 OOM canary。 |
oom_canary_size |
104857600 (100 MB) |
canary 分配并实际触及的字节数。值越大,它就越容易成为 OOM 的目标。 |
oom_canary_relaunch |
true |
canary 终止后将其重新启动 (除非是永久性初始化失败或正常关闭) ,并受以下限制约束。 |
oom_canary_max_rapid_relaunches |
10 |
为避免反复重启,在禁用自动重新启动前允许的连续快速重新启动最大次数。一旦某个 canary 的存活时间超过 oom_canary_max_backoff_seconds,该计数就会重置。 |
oom_canary_initial_backoff_seconds |
1 |
两次重新启动之间的初始延迟;每次翻倍,直到达到最大值。 |
oom_canary_max_backoff_seconds |
60 |
两次重新启动之间的最大延迟。 |
<clickhouse>
<oom_canary_enable>1</oom_canary_enable>
<oom_canary_size>104857600</oom_canary_size>
</clickhouse>可观测性
已确认的 OOM 会在
system.crash_log 中产生一行记录,其中 signal = 9,且
signal_description 中会提到 OOM Canary:
SELECT event_time, signal, signal_description
FROM system.crash_log
WHERE signal = 9 AND signal_description LIKE '%OOM Canary%'
ORDER BY event_time DESC;canary 的生命周期以及每个 OOM 响应步骤也都会记入服务器日志。