文章摘要
该文章介绍了Ubicloud团队在管理PostgreSQL服务时坚持使用严格内存过量使用设置的原因,以保护数据库免遭OOM killer的灾难性影响。文章还分享了一个内核bug导致他们暂时禁用该设置的经历,并解释了确定合适内存过量使用限制的启发式方法。
文章总结
好的,这是根据您的要求,对原文主要内容进行的中文重述,已保留关键细节并删减了与主题无关的内容。
标题:PostgreSQL 与 OOM Killer:我们为何采用严格内存过量使用
核心观点: 严格内存过量使用策略能有效保护PostgreSQL数据库,将灾难性的OOM(内存不足)终止转化为优雅的分配失败,从而避免整个数据库服务中断和数据损坏。
为什么PostgreSQL无法容忍OOM Killer:
Linux允许进程分配超过物理内存的虚拟内存。当物理内存不足时,内核会调用OOM Killer来终止一个进程以释放内存。对于大多数进程,重启即可恢复,但PostgreSQL不同。
PostgreSQL的主进程(postmaster)会为每个连接派生一个后端进程,这些后端进程共享内存段(如共享缓冲区、WAL缓冲区等)。OOM Killer不理解这种架构,它会根据启发式规则(通常是内存占用最大的进程)终止一个后端进程。如果该进程正在修改共享内存段,可能导致共享内存状态不一致,进而引发静默的数据损坏。
Postmaster检测到子进程被杀死后,会假设共享内存可能已损坏,为防止数据进一步损坏,它会终止所有其他后端进程,断开所有连接,中止所有进行中的事务,并在下次启动时进行崩溃恢复。这意味着一次OOM终止不仅影响一个连接,而是整个服务器,并且如果写入量很大,崩溃恢复可能需要很长时间,导致长时间的服务中断。
严格过量使用:尽早失败,而非灾难性失败
Linux提供三种内存过量使用策略:
- 模式0(启发式):默认模式,仅拒绝明显不合理的巨大内存请求。
- 模式1(始终):从不拒绝任何内存分配请求,最终依赖OOM Killer。
- 模式2(严格):内核跟踪所有进程的已提交虚拟内存总量(CommittedAS),并强制执行一个上限(CommitLimit)。任何会导致CommittedAS超过CommitLimit的分配请求都会被立即拒绝,并返回ENOMEM错误。
在严格模式下,当内存分配失败时,PostgreSQL能优雅地处理:后端进程向客户端报告错误,取消事务,然后继续运行。Postmaster保持运行,其他连接不受影响。这是一种常规错误,而非灾难。其代价是将后期破坏性的失败转化为早期优雅的失败。
这种策略在机器专用于PostgreSQL和少量已知辅助进程时效果最佳,因为内存使用模式是可预测的。
一个内核Bug与648GB的幽灵内存
作者团队在启用严格内存过量使用后遇到了问题:数据库出现内存不足错误,但机器上仍有大量空闲物理内存。
调查发现,一台8GB内存的服务器上,Committed_AS 显示为651GB,而实际可计入的内存仅为2.43GB,存在648GB的“幽灵”内存。通过对比不同内核版本的服务器,发现运行6.5.0内核的服务器出现此问题的概率是6.8.0内核的52倍,且问题严重程度与服务器运行时间正相关。
最终定位到Linux 6.5.0内核中的一个单字符Bug。该Bug导致在mremap操作成功时,错误地增加了Committed_AS计数器,而不是在失败时。这个计数器因此随着每次内存重映射操作而单调增长。该Bug在后续版本中被修复。
设置提交限制(CommitLimit)
作者团队使用的公式是:overcommit_kbytes = 总物理内存KB × 0.8 + 2 × 1048576,即 80%的物理内存加上2GB。
- 为什么是80%:预留20%给内核数据结构(如页表、slab缓存、网络缓冲区等)。这部分内存仍可用于页缓存,提升PostgreSQL的读取性能,且页缓存不计入
Committed_AS。 - 为什么加2GB:为PostgreSQL服务器上的辅助进程(如Prometheus、WAL-G等Go程序)预留内存。这些Go程序会预先映射大量虚拟内存,其已提交内存远大于实际使用的物理内存。调查显示,2GB的固定缓冲可以覆盖超过99%的服务器。
结论
严格内存过量使用是一个小的配置变更,能为PostgreSQL带来显著的安全提升。它将灾难性的OOM终止转化为优雅的分配失败,使每个后端进程能独立处理问题,而不会影响整个系统。尽管曾因内核Bug而暂时禁用,但它仍是健康PostgreSQL部署的关键配置。
建议在生产环境中启用vm.overcommit_memory=2,但需谨慎配置CommitLimit。设置过低会导致频繁的OOM错误,设置过高则无法获得充分保护。最佳实践是持续监控内存使用情况,在充分了解工作负载的内存特性后再启用此设置。
评论总结
根据评论内容,总结如下:
主要观点与论据:
谨慎使用严格内存超售(mode 2):Bender 强调,若已调整超售比例,使用 mode 2 可能阻止 fork,需先在 QA/Perf 环境测试,并动态调整设置而非直接写入 sysctl 配置。关键引用:"Test this in a QA/Perf environment first"、"I would just dynamically change the setting via app deployment scripts until confidence is high"。
禁用超售以避免程序被随机杀死:szmarczak 在 Windows 和 Linux 均禁用超售,因讨厌程序被随机终止,但指出许多程序提交的内存是实际使用的两倍。关键引用:"I have disabled overcommit both on Windows and on Linux. I hate having random programs being killed"、"many programs commit 2x memory than they actually use"。
混合部署场景下的困境:leononame 在部署 Go 应用和 PostgreSQL 时,从 heuristic(mode 0)切换到 strict(mode 2)导致系统不稳定,最终调整超售比例,但认为最佳方案是分离服务器。关键引用:"The whole system became a bit unstable"、"The best solution would probably be to host the backend and the database on separate servers"。
严格超售在特定场景下的合理性:ozgune(Ubicloud)同意技术内容,但认为标题过于强烈。作为托管 Postgres 提供商,他们使用严格内存超售,经验表明优于默认设置,但承认其他场景可能有意外副作用。关键引用:"we use strict memory overcommit... it's better to enable this than going with the defaults"、"I can see many other scenarios, where using strict memory overcommit would have unanticipated side-effects"。
隔离关键服务:otterley 建议将数据库等关键服务隔离到独立节点。关键引用:"it's best to isolate mission-critical services like databases on their own compute nodes"。
对超售的普遍批评:chiply314 批评超售在无交换空间的云 VM 上导致问题,认为云厂商仅建议增加内存是糟糕做法。关键引用:"Nothing worse than memory management on Hyperscaler VMs which do not use Swap"、"We lost something when we accepted that Hyperscalers just tell you to use more memory"。
Windows 无超售的合理性:wongarsu 认为微软不实施超售是明智的。关键引用:"Microsoft's decision to just not do overcommit in Windows seems sensible"。
平衡性总结:评论呈现对立观点——一方支持严格超售(如 ozgune 在特定场景下),另一方强调其风险(如 Bender、leononame),并建议隔离服务或禁用超售。多数评论认可超售需谨慎测试,且混合部署易引发问题。