PostgreSQL 内存配置 与 MemoryContext 的生命周期

news/2024/7/9 22:09:44 标签: postgresql, 数据库, 数据库开发, 内存管理

PostgreSQL 内存配置与MemoryContext的生命周期

PG/GP 内存配置

数据库可用的内存 gp_vmem

整个 GP 数据库可用的内存 gp_vmem:

>>> RAM = 128 * GB
>>> gp_vmem = ((SWAP + RAM) - (7.5*GB + 0.05 * RAM)) / 1.7
>>> print(gp_vmem / GB)
67.11764705882352

>>> RAM = 256 * GB
>>> gp_vmem = ((SWAP + RAM) - (7.5*GB + 0.05 * RAM)) / 1.7
>>> print(gp_vmem / GB)
138.64705882352942

内存上限 gp_vmem_protect_limit

每个 Segment 可用的内存上限 gp_vmem_protect_limit:

max_acting_primary_segments = num_segments + 3
gp_vmem_protect_limit = gp_vmem / max_acting_primary_segments

内存配额 gp_statement_mem

每个 Segment 可用的内存配额 gp_statement_mem:

gp_statement_mem = (gp_vmem_protect_limit * 0.9) / max_expected_concurrent_queries
gp_statement_mem = (8192 MB * .9) / 40 = 184MB

注意:实际上的配置名称为statement_mem,并且其值不能突破max_statement_mem的限制。

并发量 max_parallel_workers_per_gather

并发量与内存息息相关,并发越大启动的进程越多,消耗的内存则越大。

show max_parallel_workers; -- default 60

show max_parallel_workers_per_gather; -- default 2
set max_parallel_workers_per_gather = 5;

官方参考文档:

  • https://gp-docs-cn.github.io/docs/best_practices/sysconfig.html#topic_dt3_fkv_r4__segment_mem_config
  • https://gp-docs-cn.github.io/docs/best_practices/workloads.html

PG MemoryContext 生命周期

MemoryContext 生命周期概览

PG 的 MemoryContext 是一个树形结构,每个Query可以对应一个MemoryContext(我们可以称为query_context),query_context会创建Child行级别的MemoryContext,比如下面是Aggregate的2个典型行级别MemoryContext,分别为tuple_context、expr_context:

auto aggstate = reinterpret_cast<::AggState *>((::PlanState *) shadow_ps);
auto tuple_context = aggstate->curaggcontext->ecxt_per_tuple_memory;
auto expr_context = aggstate->tmpcontext->ecxt_per_tuple_memory;

tuple_context 生命周期

一般来说,tuple_context的生命周期比query_context的短,当火山模型中的一行数据完成吐出,或者向量化模型中的一批数据完成吐出,则tuple_context中的内存就可以释放了,tuple_context的生命周期是贯穿每一行的如下步骤:

  • 从最底层读取数据到数据。
  • 中间的一连串计算算子。
  • 结尾的将最终结果吐出给用户。

expr_context 生命周期

但是expr_context的生命周期,一般来说,比tuple_context的更短,expr_context在任何一个算子中,或两个上下依赖的算子中,均可完成其全部生命周期(从创建到销毁内存)。本质上来说,expr_context定位为临时性的内存需求,用完即可释放。

MemoryContext 典型操作

MemoryContext 上下文创建

/*
 * Create working memory for expression evaluation in this context.
 */
// src/backend/executor/execUtils.c
econtext->ecxt_per_tuple_memory =
    AllocSetContextCreate(estate->es_query_cxt,
                          "ExprContext",
                          minContextSize,
                          initBlockSize,
                          maxBlockSize);

// src/backend/utils/mmgr/aset.c
MemoryContext
AllocSetContextCreateInternal(MemoryContext parent,
                              const char *name,
                              Size minContextSize,
                              Size initBlockSize,
                              Size maxBlockSize)
{
    AllocSet set;
    ...
    // src/backend/utils/mmgr/mcxt.c
    MemoryContextCreate((MemoryContext) set,
                        T_AllocSetContext,
                        &AllocSetMethods,
                        parent,
                        name);
    ...
    return set;
}

MemoryContext 上下文切换

auto expr_context = aggstate->tmpcontext->ecxt_per_tuple_memory;
auto oldcxt = ::MemoryContextSwitchTo(expr_context);

MemoryContext 内存释放(整体)

MemoryContextReset() 函数用于释放一个 MemoryContext 中分配的所有内存:

// the tmpcontext is short-live
auto expr_context = aggstate->tmpcontext->ecxt_per_tuple_memory;
::MemoryContextReset(expr_context);

Memory 内存典型操作

内存分配 palloc

void* palloc(Size size)
{
    /* duplicates MemoryContextAlloc to avoid increased overhead */
    void       *ret;
    MemoryContext context = CurrentMemoryContext;

    AssertArg(MemoryContextIsValid(context));
    AssertNotInCriticalSection(context);
    
    if (!AllocSizeIsValid(size))
        elog(ERROR, "invalid memory alloc request size %zu", size);
    
    context->isReset = false;
    
    ret = context->methods->alloc(context, size);
    if (unlikely(ret == NULL))
    {
        MemoryContextStats(TopMemoryContext);
        ereport(ERROR,
                (errcode(ERRCODE_OUT_OF_MEMORY),
                 errmsg("out of memory"),
                 errdetail("Failed on request of size %zu in memory context \"%s\".",
                           size, context->name)));
    }
    
    VALGRIND_MEMPOOL_ALLOC(context, ret, size);
    
    return ret;
}

内存分配并填充0 – palloc0

void* palloc0(Size size)
{
    /* duplicates MemoryContextAllocZero to avoid increased overhead */
    void       *ret;
    MemoryContext context = CurrentMemoryContext;

    AssertArg(MemoryContextIsValid(context));
    AssertNotInCriticalSection(context);
    
    if (!AllocSizeIsValid(size))
        elog(ERROR, "invalid memory alloc request size %zu", size);
    
    context->isReset = false;
    
    ret = context->methods->alloc(context, size);
    if (unlikely(ret == NULL))
    {
        MemoryContextStats(TopMemoryContext);
        ereport(ERROR,
                (errcode(ERRCODE_OUT_OF_MEMORY),
                 errmsg("out of memory"),
                 errdetail("Failed on request of size %zu in memory context \"%s\".",
                           size, context->name)));
    }
    
    VALGRIND_MEMPOOL_ALLOC(context, ret, size);
    
    MemSetAligned(ret, 0, size);
    
    return ret;
}

内存重分配 repalloc

/*
 * repalloc
 *      Adjust the size of a previously allocated chunk.
 */
void* repalloc(void *pointer, Size size)
{
    MemoryContext context = GetMemoryChunkContext(pointer);
    void       *ret;

    if (!AllocSizeIsValid(size))
        elog(ERROR, "invalid memory alloc request size %zu", size);

    AssertNotInCriticalSection(context);

    /* isReset must be false already */
    Assert(!context->isReset);

    ret = context->methods->realloc(context, pointer, size);
    if (unlikely(ret == NULL))
    {
        MemoryContextStats(TopMemoryContext);
        ereport(ERROR,
                (errcode(ERRCODE_OUT_OF_MEMORY),
                 errmsg("out of memory"),
                 errdetail("Failed on request of size %zu in memory context \"%s\".",
                           size, context->name)));
    }

    VALGRIND_MEMPOOL_CHANGE(context, pointer, ret, size);

    return ret;
}

内存释放 pfree

/*
 * pfree
 *      Release an allocated chunk.
 */
void pfree(void *pointer)
{
    MemoryContext context = GetMemoryChunkContext(pointer);

    context->methods->free_p(context, pointer);
    VALGRIND_MEMPOOL_FREE(context, pointer);
}

http://www.niftyadmin.cn/n/1539696.html

相关文章

oracle经典书籍推荐 转

很多网友询问如何选择入门书籍&#xff0c;学Oracle有什么好书&#xff0c;这里给出一些常见书籍的介 绍。首先声明&#xff0c;本文只涉及国外作品&#xff0c;因为国内的作品好的极少&#xff0c;大多是拼凑之作。 提到入门学习&#xff0c;我又得搬Tom(Thomas Kyte)出来…

编译aarch64android,请问aarch64-linux-android-clang++ 支持neon 指令集编译吗?

Hi请问aarch64-linux-android-clang 支持neon 指令集编译吗&#xff1f;用neon指令集 写了一个算法&#xff0c; 相同的代码用aarch64-linux-android-gcc 可以编译过&#xff0c;aarch64-linux-android-clang 一直出错&#xff1a;pengfeiubuntu:/mnt/hgfs/share/test$ makeaar…

eclipse用svn提交文件

要更新&#xff0c;也要先同步&#xff0c;再更新了 当然要先同步&#xff0c;再提交&#xff0c;不然要被前辈给说的难受 用这张图片&#xff0c;来表示今天的开心&#xff0c;哈哈哈转载于:https://www.cnblogs.com/fuckingPangzi/p/9910661.html

visio和preject冲突_我平时用Project和visio,WPS能代替嘛?

感谢邀请。我一步一步来跟你分析解答吧。1、需求分析回答你这个问题之前&#xff0c;我们来先分析一下您的需求。project是做项目管理实施计划的&#xff0c;主要目的是进行项目管理。visio是用来绘制各种流程图、架构图、uml图等等之类的。2、现状调查在wps介绍中&#xff0c;…

live555 android,Android RTSP/UDP“RTSP/1.0 461 Unsupported transport”通过蜂窝网络(4G)

I am currently working on video streaming via RTSP/UDP for Android devices. My goal is to stream a video over a 4G cellular network. The problem I am facing is that the method used does not work with the Samsung Galaxy Core SM-G386F.我目前正在通过RTSP/UDP为…

【C++】C++中的操作符重载

C中的操作符重载使得对于类对象的操作更加方便和直观&#xff0c;但是对于各种操作符重载的规则以及语法形式&#xff0c;一直以来都是用到哪一个上stackoverflow上查找&#xff0c;在查找了四五次之后&#xff0c;觉得每次麻烦小总结一下。 操作符重载的一般语法 重载方式分为…

洛谷2014选课

题目描述 在大学里每个学生&#xff0c;为了达到一定的学分&#xff0c;必须从很多课程里选择一些课程来学习&#xff0c;在课程里有些课程必须在某些课程之前学习&#xff0c;如高等数学总是在其它课程之前学习。现在有N门功课&#xff0c;每门课有个学分&#xff0c;每门课有…

手淘android架构演进,手淘的架构设计的四个阶段以及治理

从2009年开始&#xff0c;DAU从100万增长到超过1亿&#xff0c;面临的问题、包括研发支撑所需要解决的事情各不相同。在用户量和业务复杂度的线性递增下&#xff0c;架构设计也进行了相应的演进。接下来小编给大家简单介绍关于手淘的架构设计的四个阶段以及无线架构设计的治理。…