Linux中的HugePage对数据库服务来说为什么如此重要:以PG为例

简介: Linux中的HugePage对数据库服务来说为什么如此重要:以PG为例

Linux中的HugePage对数据库服务来说为什么如此重要:以PG为例


用户经常因为OOM killer造成数据库崩溃问题来找我们寻求帮助。Out Of Memory killer会杀死PG进程,并且是我们遇到的数据库崩溃问题中首要原因。主机内存不足的原因可能有多种,最常见的有:

1) 主机上内存调整不佳

2) work_mem值全局指定过高(实例级别)。用户经常低估这种设置带来的影响

3) 连接数过高。用户忽略了一个事实,即使非活动连接也可以保留大量内存分配

4) 在同一台机器上共同托管的其他程序的资源消耗。

尽管我们曾协助调优主机和数据库,但很少花时间解释HugePage的重要性,并用数据证明它的合理性。多亏了我的朋友及同事Fernando进行反复实验,这次我忍不住这么做了。


问题


让我用一个可测试和可重复的案例解释这个问题。如果有人想以自己的方式测试案例,这可能会有所帮助。


测试环境


测试机配40CPU内核(80vCPU)和192GB内存。我不想用太多连接使这个服务器过载,所以只使用了80个连接进行测试。透明HugePage(THP)已禁用,此处不过多解释为什么将THP用于数据库服务器不是一个好主意。

为持有相对持久的连接,使用pgBouncer进行80个连接。以下是其配置:


[databases]
sbtest2 = host=localhost port=5432 dbname=sbtest2
[pgbouncer]
listen_port = 6432
listen_addr = *
auth_type = md5
auth_file = /etc/pgbouncer/userlist.txt
logfile = /tmp/pgbouncer.log
pidfile = /tmp/pgbouncer.pid
admin_users = postgres
default_pool_size=100
min_pool_size=80
server_lifetime=432000

正如我们看到的,server_lifetime参数指定一个较高的值,从而不破坏池化器到PG的连接。下面是PG的参数:

    logging_collector = 'on'
    max_connections = '1000'
    work_mem = '32MB'
    checkpoint_timeout = '30min'
    checkpoint_completion_target = '0.92'
    shared_buffers = '138GB'
    shared_preload_libraries = 'pg_stat_statements'

    使用sysbench进行测试负载:

    sysbench /usr/share/sysbench/oltp_point_select.lua --db-driver=pgsql --pgsql-host=localhost --pgsql-port=6432 --pgsql-db=sbtest2 --pgsql-user=postgres --pgsql-password=vagrant --threads=80 --report-interval=1 --tables=100 --table-size=37000000 prepare

    然后:

    sysbench /usr/share/sysbench/oltp_point_select.lua --db-driver=pgsql --pgsql-host=localhost --pgsql-port=6432 --pgsql-db=sbtest2 --pgsql-user=postgres --pgsql-password=vagrant --threads=80 --report-interval=1 --time=86400  --tables=80 --table-size=37000000  run

    第一个prepare阶段使用一个写负载,第二个是只读负载。

    此处不专注解释HugePage背后的理论和概念,而是专注于影响分析。参考https://lwn.net/Articles/717293/https://blog.anarazel.de/2020/10/07/measuring-the-memory-overhead-of-a-postgres-connection/理解一些概念。


    测试观察


    测试期间使用free命令检查内存消耗。在使用行规内存页池时,消耗量从非常低的值开始。但它一直在稳步增长。“可用”内存以更快的速度耗尽。

    最后他开始使用swap。使用vmstat采集swap活动:

    /proc/meminfo的信息显示总页表大小从最初的45MB增长到25+GB

    这不仅是内存浪费,也是一个巨大的开销,会影响程序和操作系统的整体执行。这个是80多个PG进程的Total of Lower PageTable entries大小。

    同样可以通过检查每个PG进程来验证。以下是一个示例:

    这个值*80(个连接)大概是25GB,即PageTable总大小。由于此综合基准测试通过所有连接发送几乎相近的工作负载,因此所有单个进程的值都和上面获取的值非常接近。

    下面的shell命令可以用于检查Pss(单个进程在系统总内存种实际使用量的比例)。由于PG使用共享内存,因此专注Rss没有意义。


    for PID in $(pgrep "postgres|postmaster") ; do awk '/Pss/ {PSS+=$2} END{getline cmd < "/proc/'$PID'/cmdline"; sub("\0", " ", cmd);printf "%.0f --> %s (%s)\n", PSS, cmd, '$PID'}' /proc/$PID/smaps ; done|sort -n

    如果没有 Pss 信息,就没有简单的方法来了解每个进程的内存职责

    在一个相当大的DML负载的数据库系统种,PG的后台进程如Checkpointer、Background Writer 或 Autovaccum worker将接触共享内存中更多页面,对于这些进程相应的Pss会更高。

    这里应该可以解释为什么Checkpointer, Background worker,甚至 Postmaster进程成为OOM Killer的目标。正如上面看到的,他们承担这共享内存的最大责任

    经过几个小时的执行,单个会话接触了更多共享内存页面。Pss值重排,由于其他会话分担责任,因此checkpointer负负责的更少:

    但是,checkpointer保留了最高的份额。

    由于每个会话都完成几乎相同工作,这种测试是一种特定的负载模式。这不是一个典型的应用程序负载的一个很好的近似值。我们通常看到 checkpointer和background writers承担主要责任。


    解决方案:启用HugePage


    这种臃肿的页表和相关问题的解决方案是使用HugePages。可以通过查看PG进程的VmPeak来计算出应该为HugePage分配多少内存。例如若4357是PG的PID:

      grep ^VmPeak /proc/4357/status
      VmPeak: 148392404 kB

      这里给出了需要的内存大小。将其转换2MB的页面得到大页个数:


      postgres=# select 148392404/1024/2;
      ?column?
      ----------
          72457
      (1 row)

      /etc/sysctl.conf中指定这个值到vm.nr_hugepages:

      vm.nr_hugepages = 72457

      现在关闭PG实例并执行:

      sysctl -p

      验证是否创建了请求数量的大页:

        grep ^Huge /proc/meminfo
        HugePages_Total:   72457
        HugePages_Free:    72457
        HugePages_Rsvd:        0
        HugePages_Surp:        0
        Hugepagesize:       2048 kB
        Hugetlb:        148391936 kB

        如果这个阶段启动PG,可以看到分配了HugePages_Rsvd :


        $ grep ^Huge /proc/meminfo
        HugePages_Total:   72457
        HugePages_Free:    70919
        HugePages_Rsvd:    70833
        HugePages_Surp:        0
        Hugepagesize:       2048 kB
        Hugetlb:        148391936 kB

        如果一切正常,我们更愿意PG始终使用HugePage,而不是等到以后出现问题/崩溃。


        postgres=# ALTER SYSTEM SET huge_pages = on;

        需要重启使之生效。


        使用HugePage ON进行测试


        PG启动前创建好HugePages。PG只是分配并使用他们。所以启动前后free结果不会有变化。如果他们已经可用,PG会将其共享内存分配到这些HugePage中。PG的shared_buffers是共享内存的最大占用者。

        上图中第一个free -h是PG启动前结果,第二个free -h是启动后。正如看到的,没有明显变化。

        我做了同样的测试,运行几个小时,没有任何变化。即使经过数小时运行,唯一明显变化的是将“空闲”内存转移到文件系统缓存。这是预期的,也是我们相应实现的。正如下图所示,总的“可用”内存几乎保持不变。

        总页表大小几乎保持不变:

        此时看到,HuagePages仅为61MB,而不是之前的25+GB。每个会话的Pss也大幅减少:

        我们可以看到最大的优势是 CheckPointer 或 Background Writer不再占几个GB的RAM。仅有几MB的消耗,显然他们不再是OOM Killer的的候选受害者。


        结论


        本文讨论了Linux HugePage如何潜在地从OOM Killer和相关崩溃中拯救数据库服务。可以看到有2个改进:

        1) 整体内存消耗大幅减少。如果没有HugePages,服务器几乎耗尽内存(可用内存完全耗完,开始swap)。然而一旦切换到HugePages,会有38-39GB的可用内存。节省很大

        2) 启用HugePages后,PG后台进程不会占用大量共享内存。所以他们不会轻易地成为OOM Killer的受害者

        如果系统处于OOM的边缘,这些改进可能会挽救系统。但并不是说用于保护数据库免受所有OOM的影响。

        HugePages最初于2002年用到Linux内核,用于解决需要处理大量内存的数据库系统需求。可以看到整个设计目标仍然有效。

        使用HugePages的其他间接好处:

        1) HugePages永远不会被换掉。当PG共享缓冲区在HugePages中时,它可以产生更一致和可预测的性能。将在另一篇文章中讨论。

        2) Linux使用多级页面查找方法。HugePages使用来自中间层的直接指向页面的指针实现的(2MB的大页面将直接在PMD级别找到,没有中间的PTE页面)。地址转换也相当简单。由于这是数据库中高频操作,所以收益成倍增加。

        注意:本文中讨论的HugePages是关于固定大小(2MB)的巨页。此外,作为旁注,我想提一下,多年来透明 HugePages (THP)有很多改进,允许应用程序使用 HugePages 而无需任何代码修改。THP 通常被认为是通用工作负载的常规 HugePages (hugetlbfs) 的替代品。但是,不鼓励在数据库系统上使用 THP,因为它会导致内存碎片和延迟增加。我想在另一篇文章中讨论这个主题,只是想提到这些不是 PostgreSQL 特定的问题,而是影响每个数据库系统。例如:

        1) Oracle 建议禁用 TPH。参考

        https://docs.oracle.com/en/database/oracle/oracle-database/19/ladbi/disabling-transparent-hugepages.html#GUID-02E9147D-D565-4AF8-B12A-8E6E9F74BEEA

        2) MongoDB 建议禁用 THP。参考

        https://docs.mongodb.com/manual/tutorial/transparent-huge-pages/

        3) “众所周知,对于某些 Linux 版本的某些用户,THP 会导致 PostgreSQL 性能下降。” 参考https://www.postgresql.org/docs/current/runtime-config-resource.html


        原文


        https://www.percona.com/blog/why-linux-hugepages-are-super-important-for-database-servers-a-case-with-postgresql/

        相关实践学习
        CentOS 7迁移Anolis OS 7
        龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
        目录
        相关文章
        |
        25天前
        |
        Linux 应用服务中间件 Shell
        linux系统服务二!
        本文详细介绍了Linux系统的启动流程,包括CentOS 7的具体启动步骤,从BIOS自检到加载内核、启动systemd程序等。同时,文章还对比了CentOS 6和CentOS 7的启动流程,分析了启动过程中的耗时情况。接着,文章讲解了Linux的运行级别及其管理命令,systemd的基本概念、优势及常用命令,并提供了自定义systemd启动文件的示例。最后,文章介绍了单用户模式和救援模式的使用方法,包括如何找回忘记的密码和修复启动故障。
        42 5
        linux系统服务二!
        |
        25天前
        |
        Linux 应用服务中间件 Shell
        linux系统服务!!!
        本文详细介绍了Linux系统(以CentOS7为例)的启动流程,包括BIOS自检、读取MBR信息、加载Grub菜单、加载内核及驱动程序、启动systemd程序加载必要文件等五个主要步骤。同时,文章还对比了CentOS6和CentOS7的启动流程图,并分析了启动流程的耗时。此外,文中还讲解了Linux的运行级别、systemd的基本概念及其优势,以及如何使用systemd管理服务。最后,文章提供了单用户模式和救援模式的实战案例,帮助读者理解如何在系统启动出现问题时进行修复。
        43 3
        linux系统服务!!!
        |
        4月前
        |
        Linux
        在Linux中,怎么把脚本添加到系统服务里,即用 service 来调用?
        在Linux中,怎么把脚本添加到系统服务里,即用 service 来调用?
        |
        20天前
        |
        关系型数据库 MySQL Linux
        Linux环境下MySQL数据库自动定时备份实践
        数据库备份是确保数据安全的重要措施。在Linux环境下,实现MySQL数据库的自动定时备份可以通过多种方式完成。本文将介绍如何使用`cron`定时任务和`mysqldump`工具来实现MySQL数据库的每日自动备份。
        42 3
        |
        20天前
        |
        监控 关系型数据库 MySQL
        Linux环境下MySQL数据库自动定时备份策略
        在Linux环境下,MySQL数据库的自动定时备份是确保数据安全和可靠性的重要措施。通过设置定时任务,我们可以每天自动执行数据库备份,从而减少人为错误和提高数据恢复的效率。本文将详细介绍如何在Linux下实现MySQL数据库的自动定时备份。
        31 3
        |
        1月前
        |
        Linux 数据库
        Linux服务如何实现服务器重启后的服务延迟自启动?
        【10月更文挑战第25天】Linux服务如何实现服务器重启后的服务延迟自启动?
        149 3
        |
        1月前
        |
        关系型数据库 MySQL Linux
        Linux系统如何设置自启动服务在MySQL数据库启动后执行?
        【10月更文挑战第25天】Linux系统如何设置自启动服务在MySQL数据库启动后执行?
        74 3
        |
        1月前
        |
        关系型数据库 MySQL Linux
        在 CentOS 7 中通过编译源码方式安装 MySQL 数据库的详细步骤,包括准备工作、下载源码、编译安装、配置 MySQL 服务、登录设置等。
        本文介绍了在 CentOS 7 中通过编译源码方式安装 MySQL 数据库的详细步骤,包括准备工作、下载源码、编译安装、配置 MySQL 服务、登录设置等。同时,文章还对比了编译源码安装与使用 RPM 包安装的优缺点,帮助读者根据需求选择最合适的方法。通过具体案例,展示了编译源码安装的灵活性和定制性。
        86 2
        |
        2月前
        |
        存储 NoSQL MongoDB
        基于阿里云数据库MongoDB版,微财数科“又快又稳”服务超7000万客户
        选择MongoDB主要基于其灵活的数据模型、高性能、高可用性、可扩展性、安全性和强大的分析能力。
        |
        2月前
        |
        Ubuntu Linux 网络安全
        Linux中服务管理问题
        【10月更文挑战第4天】
        26 2