PostgreSQL内存上下文[翻译]

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
简介: PostgreSQL内存上下文[翻译]

PostgreSQL内存上下文


PG使用共享内存在多进程之间进行数据共享。使用动态共享内存段dynamic shared memory segments在并行workers之间进行数据交换,这个内存在启动时分配固定大小。但是PG后端进程必须管理私有内存用于处理SQL语句。本文,介绍PG如何使用memory context,即内存上下文,来管理私有内存;以及如何检查内存使用情况。这对于编写服务器代码的人来说很有意思,但我要重点关注用户如何理解和调试SQL语句的内存消耗。


1.什么是内存上下文


PGC语言编写,C语言的内存管理比较棘手,必须显式释放所有动态分配的内存。这就特别容易造成内存泄漏了,导致不断增加内存消耗。对于PG后端这样长期存在的进程来说是致命的。

为了减少内存泄漏,PG使用内存上下文管理自己的内存。内存上下文是可以按需增长的内存块。在PG中不直接调用malloc申请内存,而是从内存上下文中申请。根据需要,PG会扩展内存上下文。

内存上下文的优势:可以通过删除内存上下文,一次性释放所有内存。这就意味着不再需要追踪分配的内存,关注什么时候释放了,简化了内存管理,降低了内存泄漏的风险。PG查询执行器在开始处理一个语句时,创建ExecutorState context。如果需要申请内存,则从该内存上下文中申请。语句执行完时,执行器会删除ExecutorState,在查询执行结束后,不必担心内存泄漏。源码src/backend/utils/mmgr/README中详细介绍了内存上下文的设计与使用。


2.内存上下文的组织


内存上下文形成一个层次结构。最顶层的内存上下文是TopMemoryContext,存在于后台进程的整个生命周期。其他任何一个内存上下文都有一个父节点。当删除一个内存上下文时,会递归删除所有后继内存上下文。因此,不需要频繁明确地释放内存。如果在较短时间内需要几个内存块,例如处理执行计划的某个步骤,可以在ExecutorState中再创建一个内存上下文,在该步骤执行完时将其删除。如果执行器在此之前终止,则该内存上下文中任何内存都不会泄漏。

重要的内存上下文

TopMemoryContext

内存上下文的最顶层,不需要删除。

CacheMemoryContext

包含数据库元数据的缓存以及执行计划的换岑。如果数据库包含多个对象(例如表分区),或者有许多prepared语句,则会占用更多空间

MessageContext

包含来自客户端的语句,有时还包含执行计划和解析数据

PortalContext

与当前语句关联的内存(称为portal或者cursor

3.一个SQL语句使用多少内存


理论上执行计划的每一步都会被work_mem限制,但是不足以评估内存的使用。

1、单个语句可能有很多内存密集型执行步骤,因此会分配work_mem多次;

2、如果语句使用并行查询,会创建动态共享内存段,work_mem并不统计这个;

3PG13之前,bytea二进制数据或者大PostGIS几何图形,会驻留在内存中,也不被work_mem限制

有一些方法可以帮助查看内存上下文中存储了多少内存。


3.1 pg_backend_memory_contexts查看内存上下文使用


pg_backend_memory_context视图限制了当前会话拥有的所有内存上下文。只能在语句之间查询该视图,但在执行SQL时查看才会更有用。为此,可以创建一个函数,将其构建到SQL语句中:

    CREATE FU
    CREATE FUNCTION dump_my_mem() RETURNS void
       LANGUAGE plpgsql AS
    $$DECLARE
       r record;
    BEGIN
       FOR r IN
          SELECT name, ident, level, total_bytes
          FROM pg_backend_memory_contexts
       LOOP
          RAISE NOTICE '% % % %',
             repeat('  ', r.level - 1),
             r.name,
             r.total_bytes,
             r.ident;
       END LOOP;
    END;$$;

    3.2 pg_log_backend_memory_contexts()记录内存上下文使用


    pg_log_backend_memory_context(integer)函数可以将任意会话的内存上下文当前状态写入日志文件。参数是进程ID,可以通过pg_stat_activity查看。默认仅超级用户可以调用整个函数,但是你可以GRANT EXECUTE权限给其他用户。

    通过这种方法,可以方便地检查长时间运行SQL的内存使用。问题是一个消耗大量内存的语句不需要长时间运行。捕捉一个简短的语句比较棘手。


    3.3 debug来记录内存使用


    如果想要检查内存使用,可以通过debug的方式精确控制语句的执行点。但需要熟悉PG代码,并gdb一个进程。

    首先看下进程ID,我们使用12345作为一个例子:

      gdb /path/to/postgresql/bin/postgres 12345
      GNU gdb (GDB) Fedora Linux 13.1-3.fc37
      Copyright (C) 2023 Free Software Foundation, Inc.
      [...]
      (gdb)

      然后打个断点,一个有用的函数是ExecutorEndPG处理一个语句结束点:


      (gdb) break ExecutorEnd
      Breakpoint 1 at 0x783271: file execMain.c, line 471.
      (gdb) cont
      Continuing.

      执行有问题的语句,一旦执行到断点,就会触发内存上下文的dump

        Breakpoint 1, ExecutorEnd (queryDesc=0x2333fd8) at execMain.c:471
        471 if (ExecutorEnd_hook)
        (gdb) print MemoryContextStats(TopMemoryContext)
        $1 = void

        这会将内存上下文转储到日志文件。然后可以detach该进程,退出GDB


        (gdb) detach
        Detaching from program: /path/to/postgresql/bin/postgres, process 12345
        [Inferior 1 (process 12345) detached]
        (gdb) quit

        4.评估PG总内存使用


        一个繁忙的数据库将有许多会话同时运行,很难说会有多少连接,以及他们执行的是简单还是复杂的语句。因此很难预测到PG使用多少内存。恰当地说,你所知道的work_mem的一切都是错误的,很显然Christophe Pettus提出了自己的公式:


        50%free memory + 文件系统buffers/连接数


        可以看到,连接数有着至关重要的作用。如果想获得良好性能,需要使用大小合适的连接池。毕竟,足够大的work_memnon-trival SQL语句良好性能的重要条件。


        5.PG内存不足


        我们当然不想遇到内存不足的情况,但是一旦发生,后果很大程度上取决于如何配置操作系统内核。使用默认配置,Linux将在内存耗尽时调用“out-of memory killer”。这个不友好的内核组件将向某些后台进程发送SIGKILL信号,无条件终止进程并释放内存。PG进程过早死亡,会断开所有连接,并导致崩溃恢复。崩溃恢复意味着直到PG恢复到上次最近的checkpoint,才能对外服务。

        避免这种崩溃的正确方法是:设置内核参数vm.overcommit_memory2和调整vm.overcommit_ratio。然后回得到一个常规out of memory”错误,PG会将内存上下文dump到日志文件。该内存上下文转储非常有用,有助于理解后格SQL在哪里分配了所有的内存。


        6.总结


        拥有PG如何使用内存上下文管理私有内存的概念非常重要,即使你不是一个内核开发者。正确配置有助于理解内存上下文,同时也介绍了一些视图和函数来帮助检查内存上下文。


        原文


        https://www.cybertec-postgresql.com/en/memory-context-for-postgresql-memory-management/


        相关实践学习
        使用PolarDB和ECS搭建门户网站
        本场景主要介绍基于PolarDB和ECS实现搭建门户网站。
        阿里云数据库产品家族及特性
        阿里云智能数据库产品团队一直致力于不断健全产品体系,提升产品性能,打磨产品功能,从而帮助客户实现更加极致的弹性能力、具备更强的扩展能力、并利用云设施进一步降低企业成本。以云原生+分布式为核心技术抓手,打造以自研的在线事务型(OLTP)数据库Polar DB和在线分析型(OLAP)数据库Analytic DB为代表的新一代企业级云原生数据库产品体系, 结合NoSQL数据库、数据库生态工具、云原生智能化数据库管控平台,为阿里巴巴经济体以及各个行业的企业客户和开发者提供从公共云到混合云再到私有云的完整解决方案,提供基于云基础设施进行数据从处理、到存储、再到计算与分析的一体化解决方案。本节课带你了解阿里云数据库产品家族及特性。
        目录
        相关文章
        |
        关系型数据库 数据库 PostgreSQL
        PostgreSQL 内存表可选项 - unlogged table
        标签 PostgreSQL , 内存表 , unlogged table 背景 内存表,通常被用于不需要持久化,变更频繁,访问RT低的场景。 目前社区版本PostgreSQL没有内存表的功能,postgrespro提供了两个插件可以实现类似内存表的功能。
        3100 0
        |
        SQL 存储 分布式计算
        PolarDB-X 是如何用15M内存跑1G的TPCH
        本文主要对PolarDB-X计算层的内存管理进行分析,这有助于大家有PolarDB-X有更深入的理解。 PolarDB-X内存管理机制的设计,主要为了几类问题: 1. 让用户更容易控制每个查询的内存限制; 2. 预防内存使用不当,导致内存溢出进而引发OOM; 3. 避免查询间由于内存争抢出现相互饿死现象; 4. 避免AP Workload使用过多内存,严重拖慢TP Workload
        824 0
        PolarDB-X 是如何用15M内存跑1G的TPCH
        |
        SQL 缓存 监控
        【巡检问题分析与最佳实践】RDS MySQL 内存使用问题
        实例内存使用率和buffer pool命中率是RDS MySQL的关键指标之一,如果内存使用率过高会有OOM风险,如果buffer pool命中率低,大量的数据页无法命中buffer pool中缓存的数据页,需要从存储读取数据,造成IO吞吐增加和延迟增加。
        【巡检问题分析与最佳实践】RDS MySQL 内存使用问题
        |
        关系型数据库 PostgreSQL 索引
        PostgreSQL cheat functions - (内存上下文\planner内容\memory context等常用函数)
        标签 PostgreSQL , memory context , plan , pid signal , ... 背景 https://github.com/MasaoFujii/pg_cheat_funcs Fujii Masao MasaoFujii PostgreSQL comm...
        1271 0
        |
        监控 关系型数据库 MySQL
        如何定位RDS MySQL内存使用率高?
        打开performance_schema 控制台可开 打开内存监控 update performance_schema.setup_instruments set enabled = 'yes' where name like 'memory%'; 查看监控 select * from sys.
        3930 0
        |
        新零售 关系型数据库 测试技术
        PostgreSQL技术周刊第10期:PostgreSQL 调用 Rust 函数内存耗用研究
        PostgreSQL(简称PG)的开发者们:云栖社区已有5000位PG开发者,发布了3000+PG文章(文章列表),沉淀了700+的PG精品问答(问答列表)。 PostgreSQL技术周刊会为大家介绍最新的PG技术与动态、预告活动、最热问答、直播教程等,欢迎大家订阅PostgreSQL技术周刊。
        3172 0
        |
        关系型数据库 PostgreSQL Rust
        |
        1月前
        |
        存储 JSON 监控
        Higress Controller**不是将配置信息推送到Istio的内存存储里面的**。
        【2月更文挑战第30天】Higress Controller**不是将配置信息推送到Istio的内存存储里面的**。
        14 1
        |
        1月前
        |
        存储 C语言
        C语言--------数据在内存中的存储
        C语言--------数据在内存中的存储
        26 0
        |
        1天前
        |
        存储 算法
        【三种方法】求一个整数存储在内存中二进制中的1的个数附两道课外练习题
        【三种方法】求一个整数存储在内存中二进制中的1的个数附两道课外练习题
        6 0

        热门文章

        最新文章