OOM canary 仍处于 Experimental 阶段,默认处于禁用状态。在完成生产环境验证之前,
其行为在不同 ClickHouse 版本之间可能会发生变化。
概述
SIGKILL 终止一个进程——通常是占用内存最多的那个;而在专用主机上,这往往就是 clickhouse-server 本身。这样一来,整个服务器会直接丢失,而不是获得恢复的机会。
OOM canary 改变了“谁先死”。它会运行一个小型的牺牲性子进程,使其成为最容易被 OOM 选中的目标,这样内核杀掉的就是它,而不是服务器。随后,服务器会检测到该进程已死亡,确认这是一次 OOM 事件,并缓解内存压力,从而让自己存活下来。
canary 不会提高任何 memory limit,也不能替代正确设置的 limits (参见 内存 overcommit 和 max_server_memory_usage) 。它是最后一道防线:用少量固定的内存,换取在内存突增时存活下来的机会。
工作原理
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 会自行禁用。
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 目标的作用。
配置
可观测性
system.crash_log 中产生一行记录,其中 signal = 9,且
signal_description 中会提到 OOM Canary: