背景

最近排查和解决了几处 HybridDB for PostgreSQL 内存泄漏的BUG。觉得有一定通用性。
这期分享给大家一些实现细节和小技巧。

阿里云上的 HybridDB for PostgreSQL 是基于 PostgreSQL 开发,定位于 OLAP 场景的 MPP 架构数据库集群。它不少的内部机制沿用了 PostgreSQL 的实现。其中就包括了内存管理机制 MemoryContext。

一:PostgreSQL 内存管理机制

PostgreSQL 对内存的使用方式主要分两大块

1. shared_buffer 和同类 buffer。 简单的说 shared_buffer 用于存放数据页面对应数据文件中的 block,这部分内存是 PostgreSQL 中各进程共享。这部分不在本文讨论。
2. MemoryContext 以功能为单位组织起来的树形数据结构,不同的阶段使用不同的 MemoryContext。

1. MemoryContext 的作用

简单的说 MemoryContext 的存在是为了更清晰的管理内存

  • 合理管理碎片小内存。频繁的向 OS 申请和释放内存效率是很差的。MemoryContext 会以 trunk 为单位向 OS 申请成块的内存,并管理起来。当程序需求小内存时从 trunk 中分配,用完后归还给对应的 MemoryContext ,并不归还给 OS。
  • 赋予内存功能和生命周期属性
    • 以功能为单位管理内存。不同功能和阶段使用对应的 MemoryContext。
    • TopTransactionContext:一个事务的生命周期,事务管理相关数据放在 TopTransactionContext,当一个事务提交时该上下文被整个释放。
    • ExprContext PostgreSQL 以行为单位处理数据,每一行数据的表达式计算都会在 ExprContext 完成,每处理完一行都会重置对应的 ExprContext。
  • 树形的 MemoryContext 结构
    • 不同功能间的 MemoryContext 是以为树为单位组织起来的
    • 每个数据库后端进程顶层是 TopMemoryContext
    • TopMemoryContext 下有很多子 Context
      • 缓存相关的 CacheMemoryContext;
      • 本地锁相关的 LOCALLOCK hash;
      • 当前事务相关的 TopTransactionContext
      • 注意 CacheMemoryContext 为何不属于 TopTransactionContext,那是由于 Cache 是独立于事务存在的,事务提交不影响 Cache 的存在。
    • 删除或重置一个 MemoryContext,它的子 MemoryContext 也一并被删除或重置。

2. 不同模块的 MemoryContext

你可能明白了,实现不同的模块时,对待内存的方式可能区别很大。
比如:

1. 执行器在做表达式计算时,一些诸如字符串类型数据处理的函数,大多会比较随意的使用 palloc 分配内存,但直到函数返回,却并没有释放它们。
2. 在处理缓存模块处理数据时,却倍加小心的释放内存。

这是由于:

1. 执行器对数据的处理是以行为单位,都在 ExprContext 中,每处理完一行,会重置 ExprContext,以此释放相关的内存。
2. 缓存的生命周期很长,不会定期重置整个 MemoryContext。哪怕少量的内存泄漏,积攒的后果都很严重。这部分的实现容易出问题,也不好排查。

3. 常见的内存问题

虽然有很好的内存管理机制,但进程中内存间没有强隔离,也可能出现内存问题。

造成内存泄漏的原因很大可能是:

1. 在较长生存周期的 MemoryContext 中正常处理流程中没有释放内存。
2. 由于发生了异常,跳转到在异常处理阶段没有释放内存。
3. 没有使用内存管理机制,使用 OS 调用 malloc,free 处理内存(某些实现不合理的插件中可能出现)。
4. 在不正确的 MemoryContext 分配了内存,导致内存泄漏或数据丢失。
5. 写内存越界,这是最难找的问题,很容易造成数据库崩溃。

4. 问题排查小技巧

针对内存泄漏,常用两种方法排查

1. valgrind 最常见的大杀器,开发人员都懂的。这里就不详细介绍了。

2. 使用 GDB 也能大致定位问题

2.1 这是一段脚本,我们把它保存成文本文件(pg_debug_cmd)

define sum_context_blocks
set $context = $arg0
set $block = ((AllocSet) $context)->blocks
set $size = 0
while ($block)
set $size = $size + (((AllocBlock) $block)->endptr - ((char *) $block))
set $block = ((AllocBlock) $block)->next
end
printf "%s: %d\n",((MemoryContext)$context)->name, $size
enddefine walk_contexts
set $parent_$arg0 = ($arg1)
set $indent_$arg0 = ($arg0)
set $i_$arg0 = $indent_$arg0
while ($i_$arg0)
printf " "
set $i_$arg0 = $i_$arg0 - 1
end
sum_context_blocks $parent_$arg0
set $child_$arg0 = ((MemoryContext) $parent_$arg0)->firstchild
set $indent_$arg0 = $indent_$arg0 + 1
while ($child_$arg0)
walk_contexts $indent_$arg0 $child_$arg0
set $child_$arg0 = ((MemoryContext) $child_$arg0)->nextchild
end
endwalk_contexts 0 TopMemoryContext

2.2 获得疑似内存泄漏的进程PID,定时触发执行下面的 shell

gdb -p $PID < pg_debug_cmd > memchek/MemoryContextInfo_$(time).log

2.3 分析日志文件

日志文件以 MemoryContext 树的形式展示了一个时间点该进程的内存分配情况。根据时间的积累,可以很容易判断出哪一些 MemoryContext 可能存在异常,从而为内存泄漏指明一个方向。

(gdb)
TopMemoryContext: 149616pgstat TabStatusArray lookup hash table: 8192TopTransactionContext: 8192TableSpace cache: 8192Type information cache: 24480Operator lookup cache: 24576MessageContext: 32768Operator class cache: 8192smgr relation table: 24576TransactionAbortContext: 32768Portal hash: 8192PortalMemory: 8192PortalHeapMemory: 1024ExecutorState: 24576SRF multi-call context: 1024ExprContext: 0ExprContext: 0ExprContext: 0Relcache by OID: 24576CacheMemoryContext: 1040384pg_toast_2619_index: 1024....pg_authid_rolname_index: 1024WAL record construction: 49776PrivateRefCount: 8192MdSmgr: 8192LOCALLOCK hash: 8192Timezones: 104128ErrorContext: 8192

最后,文章的参考资料中也提供了一种类似的方法,供各位参考。

总结

PostgreSQL 内存管理机制的实现比较复杂,但用起来确却很简单,有一种特别的美感,推荐大家了解一下。

参考资料

  1. PostgreSQL Developer_FAQ

HybridDB · 源码分析 · MemoryContext 内存管理和内存异常分析相关推荐

  1. 【Linux 内核 内存管理】内存映射相关数据结构 ③ ( vm_area_struct 结构体成员分析 | shared 成员 | anon_vma_chain 成员 | anon_vma 成员 )

    文章目录 一.vm_area_struct 结构体成员分析 1.shared 成员 2.anon_vma_chain 成员 3.anon_vma 成员 二.vm_area_struct 结构体完整源码 ...

  2. 美容院SPA会员管理系统源码 美容行业会员管理收银系统源码

    美容院SPA会员管理系统源码 美容行业会员管理收银源码 开发语言:PHP 数据库:MySQL 开发工具:phpstrom 源码类型:全开源免费分享 该套会员管理系统是一套通用性强.功能强大的会员管理系 ...

  3. C#源码 +sqlserver包含库存管理、销售管理

    C#源码 +sqlserver包含库存管理.销售管理.采购管理.委外加工管理.生产管理.财务管理.基础数据管理.系统管理.文件管理.设备管理.权限管理等模块,完全开源,支持二次开发. 编号:85765 ...

  4. 连锁门店收银系统源码之要货管理--进销存功能逻辑解析03

    连锁多门店收银系统源码之要货管理--进销存逻辑解析03 一,连锁多门店收银系统源码的要货管理综述 1,下级连锁门店(直营店或加盟店)在需要进货时,向总部操作"要货申请" 2,连锁总 ...

  5. 第14课:Spark Streaming源码解读之State管理之updateStateByKey和mapWithState解密

    第14课:Spark Streaming源码解读之State管理之updateStateByKey和mapWithState解密 /* 王家林老师授课http://weibo.com/ilovepai ...

  6. 内存分配器 mysql_MySQL内存管理,内存分配器和操作系统的示例分析

    MySQL内存管理,内存分配器和操作系统的示例分析 发布时间:2021-01-08 14:06:39 来源:亿速云 阅读:79 作者:小新 这篇文章主要介绍MySQL内存管理,内存分配器和操作系统的示 ...

  7. dpdk内存管理之rte_eal_hugepage_init()函数分析

    dpdk版本:dpdk-stable-16.11.11 今天我们来看一下rte_eal_hugepage_init() 函数都干了哪些事. 1.计算大页总数 在调用rte_eal_hugepage_i ...

  8. C++ 内存管理中内存泄漏问题产生原因以及解决方法

    C++内存管理中内存泄露(memory leak)一般指的是程序在申请内存后,无法释放已经申请的内存空间,内存泄露的积累往往会导致内存溢出. 一.内存分配方式 通常内存分配方式有以下三种: (1)从静 ...

  9. 【Linux 内核 内存管理】内存管理架构 ④ ( 内存分配系统调用过程 | 用户层 malloc free | 系统调用层 brk mmap | 内核层 kmalloc | 内存管理流程 )

    文章目录 一.内存分配系统调用过程 ( 用户层 | 系统调用 | 内核层 ) 二.内存管理流程 一.内存分配系统调用过程 ( 用户层 | 系统调用 | 内核层 ) " 堆内存 " ...

  10. SAP专家培训之Netweaver ABAP内存管理和内存调优实践

    培训者:SAP成都研究院开发人员Jerry Wang 1. Understanding Memory Objects in ABAP Note1: DATA itab WITH HEADER LINE ...

最新文章

  1. 困扰多日的C#调用Haskell问题竟然是Windows的一个坑
  2. 第七课.Logistic回归算法
  3. Windows 10 powershell 中文乱码解决方案
  4. air什么意思中文_Air译中文是什么意思,the air中文是什么意思
  5. 洛谷.4245.[模板]任意模数NTT(MTT/三模数NTT)
  6. 游戏编程新手教程:怪物AI设计简述
  7. 最热门的10个Java微服务框架
  8. obs之libfaac编码
  9. list所有元素相加 python_什么是序列,Python序列详解(包括序列类型和常用操作)...
  10. Spring MVC @JsonView注解使用
  11. Unity3D 内存释放 垃圾回收
  12. CentOS hping3安装和应用
  13. pda扫描枪屏幕_PDA扫描枪的介绍
  14. 视频切割(解决音视频不同步问题)
  15. 手机怎么识别图中文字?这两个方法靠谱
  16. 【SEO优化,网络营销】刘克亚《利润腾挪》,一分钟销售51000元的书
  17. Hex Fiend很强大
  18. 网贷黑名单查询,通过身份证号,或者手机号查询自己的网贷情况。
  19. 手把手搭建属于自己的搜索引擎——SearX
  20. sort、asort、ksort 三者说明

热门文章

  1. IE的安全性设定增加“我的电脑”的安全性设定
  2. 与流氓软件的一次艰苦“奋战”
  3. Hello world!
  4. Wireless-N Configuration
  5. PreparedStatement
  6. electron打包可选择安装位置,可自动更新
  7. ITFriend站点内測公測感悟
  8. 【JDK7】新特性(2) 语法
  9. 创新工场有哪些失败项目?不要只看着成功
  10. 针对19端口的Chargen进行Dos***