暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

内存管理调研

原创 手机用户2895 2025-01-22
199

1. 常见的内存管理模式

  • Per-class allocators
    常见的mempool就是这种实现,pool是一块大内存,分割成一个个的固定大小的对象。polarstore采用的内存管理就是这种方式,不同的对象归属到不同的mempool。
    这种方式的优点是实现非常简单,性能也相当好,缺点是内存消耗的放大很严重。

  • Regions
    Imci的Arena以及innodb的mem_heap_t就是这类实现,提前分配一整块内存,分配对象时,只需要指针在内存上移动。
    这种实现是所有实现里性能最好的,缺点是只有malloc跟freeall的接口,没办法free一个特定的对象,长时间运行的任务,内存无法及时释放,导致系统内存水位高。MySQL经常出现slow log导致的OOM,就是regions的负作用。

  • General-purpose allocator
    tcmalloc, jemalloc,以及libc自带的ptmalloc都归为这类,与上面两类最大的区别就是支持variable length的malloc/free,以及支持free任意一块内存。

显然,对于PolarDB IMCI来讲,Per-class 以及Regions都有难以克服的缺陷。我们不可能对每个类型的class分配一个mempool。数据分析场景一条sql跑几十分钟不罕见,期间一直不释放内存,其他短SQL很容易分配不出内存。

因此,我们只考虑General-purpose的实现,既要speed,也要low consumption。

通过对两款典型的General-purpose allocalor:mimalloc以及jemalloc的分析,一方面调研能否直接引入为IMCI所用,另一方面如何吸收优秀的设计,简化无用的实现。

  • mimalloc比较新,2019年推出,性能数字惊艳,吊打jemalloc,核心代码3500行。但缺乏large buffer的优化。

调研文档:LINK

  • jemalloc有facebook背书,配套工具完善,mariadb引入jemalloc做为默认的分配器,aliyun rds随后跟进,PolarDB也一直沿用下来。jemalloc风评好,ptmalloc被各种嫌弃,我个人的看法是,业内普遍在用古早版本的glibc,新版本scalibility方面的大量工作缺乏宣传。根据2019年微软研究院的测试结果https://www.microsoft.com/en-us/research/uploads/prod/2019/06/mimalloc-tr-v1.pdf,jemalloc在benchmark场景表现惊艳,真实场景如redis,表现不如glibc。

调研文档:(TODO)

2. 针对应用定制的Allocator

相当一部分系统软件会自己定制内存分配器。定制allocator最大的好处可以聚焦应用本身的common case,极大降低复杂度。针对系统使用内存的模式,有具体场景更容易针对性的优化,性能上也不难取得优势。

以MySQL为例,THD本身是query arena的子类,基类重载了new/delete,同query arena预先分配page,allocate时在page上切割内存,THD析构时deallocate,最大的好处是统一了生命周期,杜绝掉内存泄露的可能性,内存分配的性能是所有其他方案的天花板。负作用是defer freeing会导致内存水位高。

MySQL存储引擎的page由buffer pool管理,其他对象也支持从buffer pool分配,支持不超过page size大小的分配,实例启动时mmap好所有内存,第一次写入时映射好物理页,实例退出时munmap。page 淘汰后,归还freelist,几乎不依赖虚拟内存,自然不会有缺页中断。通过buffer pool绕过了page cache以及kernel buddy system,我认为是MySQL点查询性能压过PG的主要原因。

另一款AP数仓Impala,早期版本依赖tcmalloc,被free性能折磨后

Reduced reliance on TCMalloc, which isn't suited to management of large buffers (e.g. see IMPALA-2800)

2.0版本选择自己开发buffer pool (https://issues.apache.org/jira/browse/IMPALA-3200?jql=project%20%3D%20IMPALA%20AND%20text%20~%20%22buffer%20manage%22)。

其特点是执行层,存储的所有内存都从buffer pool分配,支持variable-length。除了profile,statistic,safety等功能外,比较亮眼的是支持了spilling。(https://issues.apache.org/jira/browse/IMPALA-3202)。

Custom allocator除了实现简单以及性能优势外,还有很重要的一点是可以根据应用的需求定制,impala可以在内存高水位(over 80% limit)时,后台线程定时把超过水位的memory归还给OS。一方面尽量延迟free内存来保证性能,另一方面,防止内存水位过高,引起用户焦虑。

不同的系统内存使用的性能瓶颈也不一样,以IMCI为例,多个客户的场景,瓶颈都是big buffer的处理上,通用的内存分配器认为big buffer是很少reuse的,因此释放很激进。

gnu libc的allocator ptmalloc,超过一定大小的内存,直接通过mmap跟os申请,free后直接返还os。

jemalloc认为large memory(超过4MB)是less reuse的,为了减少碎片,large memory的分配走单独的arena,为了降低内存水位,延迟释放等优化都会忽略掉large memory's dedicated arena。

总结一下,imci依赖jemalloc带来的几个问题是

  1. dedicated arena影响了scalibility。

  2. purge eargly,导致频繁的munmap。

结果是系统对大表或宽表大量扫描时,通常频繁分配large buffer,必然导致并发能力变差,大量出现slow log。

易仓/advance都是jemalloc的victim。 

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论