Linux 内存超额使用对 PostgreSQL 的影响

John Doe 十一月 20, 2024

摘要:尽管内存超额使用可能对其他应用程序有用,但对您的 PostgreSQL 数据库来说却是个坏消息。本文将指导您正确地配置内核,并讨论对容器化设置的影响。

目录

什么是内存超额使用?

Linux 会尝试节省内存资源。当您从内核请求内存块时,Linux 不会立即为您保留该内存。您得到的只是一个指针,和您可以在目标处使用内存的承诺。内核仅在您实际使用内存时分配内存。这样,如果您请求 1MB 内存,但只使用了其中的一半,则另一半永远不会被分配,并且可用于其他进程(或内核页面缓存)。

超售是世界各地航空公司长期以来一直在使用的概念。他们卖的座位比飞机上实际的座位多。根据经验,航空公司知道有些乘客没有出现在航班上,因此超售可以让他们获得更多利润。默认情况下,Linux 会做同样的事情:它会分配(“提交”)比机器中实际可用内存更多的内存,预期不是所有进程都会使用它们分配的所有内存。这种内存超额使用非常适合有效地使用资源,但它有一个问题:如果所有航班乘客都出现,换句话说,如果进程实际使用的内存多于可用内存,该怎么办?毕竟,您不能为计算机进程提供退款或免费的过夜酒店房间。

如果一个进程试图使用它已经分配的内存,但内核无法提供它,那么 Linux 只能做一件事:它会激活一个叫做内存不足杀手的组件,该组件会杀死一个使用大量内存的倒霉进程。终止进程会释放它们的内存,而 Linux 现在就拥有了这些内存。

为什么内存超额使用是 PostgreSQL 的一个问题?

当 PostgreSQL 发生崩溃时,假如有什么东西杀死了它的一个子进程,postmaster 进程会变得非常不稳定:子进程可能在一段临界代码区的中间死亡,并让共享资源(主要是内存)处于不一致的状态。避免数据损坏的唯一安全方法是,发生崩溃和随后的崩溃恢复。

在一个繁忙的数据库上,崩溃恢复可能需要相当长的时间 — PostgreSQL 必须重放自最新检查点以来修改数据写入的所有 WAL。在此期间,您无法连接到数据库。任何连接尝试都会返回错误 “数据库系统处于恢复模式”。

禁用 Linux 内存超额使用

您可以使用参数vm.memory_overcommit,配置 Linux 内核的行为。有关详细信息,请参阅 Linux 内核文档。默认值 0 表示 “在某些(自由)限制内超额使用”,1 表示 “无限制的超额使用”,2(“禁用”)是我们想要的设置。您可以更改设置,以用户root身份执行

sysctl vm.overcommit_memory=2

要使更改持久化,请编辑/etc/sysctl.conf,或在/etc/sysctl.d中创建一个新文件,添加下面内容

vm.overcommit_memory = 2

通过以root运行下面命令来激活设置:

sysctl --system

禁用内存超额使用时,请务必确保当前内存未超额使用。否则,您的机器可能会变得不可用。要验证这一点,您可以检查/proc/meminfo中的Committed_AS条目:它应该小于 Linux 在禁用内存超额使用后准备提交到进程的内存量。

如果没有内存超额使用,如果进程尝试分配的内存超过内核准备处理的内存,Linux 将返回错误 “内存不足”(ENOMEM)。如果 PostgreSQL 进程收到此错误,则运行的语句将失败,并显示错误代码 53200,但整个数据库仍可运行。

不要忘记配置 Linux 将提交的内存量

如果禁用内存超额使用,则还必须配置 Linux 分配给进程的内存量。默认情况下,Linux 参数vm.nr_hugepagesvm.overcommit_ratio,会决定内核将提交的内存量。公式很复杂:

committable memory = swap + (RAM - vm.nr_hugepages * huge page size) * vm.overcommit_ratio / 100

内存 RAM 和交换空间的大小,可以从操作系统命令free的输出中找到。

现在vm.overcommit_ratio的默认值是 50,现代机器的内存比交换空间大得多。这意味着在系统真正耗尽内存之前,您将收到内存不足的错误。所以你绝对应该调整限制。如果你不想纠结上面的公式,有一种更简单的方法来配置可用内存量:你可以根据这个公式来设置vm.overcommit_kbytes

vm.overcommit_kbytes = available RAM - vm.nr_hugepages - swap

如果设置vm.overcommit_kbytes,它将覆盖vm.overcommit_ratio。您可以像我在上一节中描述vm.overcommit_memory的那样,设置这些参数。

如何避免 PostgreSQL 中出现内存不足

如果禁用内存超额使用,则可以防止最坏的情况发生。但您也不希望 PostgreSQL 语句中出现内存不足的错误。那么,您应该如何配置 PostgreSQL,以防止它使用的内存超过可用内存呢?不幸的是,答案并不简单。很容易估计的是,PostgreSQL 将要使用的共享内存量。最大的份额将是共享缓冲区。该缓存的大小由 PostgreSQL 参数shared_buffers确定。PostgreSQL 在服务器启动时会分配该内存,并且从不调整其大小。要确定 PostgreSQL 实例对共享内存的总体需求,您可以查看 PostgreSQL 参数shared_memory_size。所以这部分相当容易。

很难估计的是,PostgreSQL 进程将使用的私有内存量。确定私有内存使用量的 PostgreSQL 参数是work_mem,但这不是整个实例的全局限制。相反,它用于确定执行计划中的单个步骤可以使用多少内存。所以单个语句最终可能会使用多个work_mem!那么,如何调整work_mem的大小,以避免内存不足呢?正如您所料,没有简单的答案。但是,按照下面粗略的经验法则,通常就足够了:

shared_buffers + 2 * work_mem * maximum number of connections  available memory

顺便说一句,这个公式强调了适度大小的连接池对良好性能的重要性:您拥有的连接越少,您就可以将更多内存分配给work_mem,并且大量的内存将会加速许多重要的 SQL 语句。

容器化环境中的内存超额使用

在容器内运行 PostgreSQL 已成为一种流行的选择。容器允许您管理资源,并且通过适当的编排,创建和销毁 PostgreSQL 实例可以变得很简单。每个容器镜像都有一份软件和系统库的副本,并且可以限制一个容器可以消耗的系统资源。但是,主机上的所有容器都共享一个组件:操作系统内核。因此,如果您在内核中禁用内存超额使用,这会影响主机上的所有容器。

如果要在容器中运行 PostgreSQL,则应在它们所属的主机上运行 PostgreSQL 容器。这样,您就可以配置 Linux 内核,以与 PostgreSQL 配合使用。其他软件可能依赖于内存超额使用,并且可能会在禁用内存超额使用的情况下遇到问题。

结论

为了 PostgreSQL 的稳定性,在 Linux 内核上禁用内存超额使用至关重要。不要忘记适当地调整vm.overcommit_kbytes。为避免内存不足,请保守地设置shared_bufferswork_mem