Postgresql源码(160)新的共享内存注册机制,为动态调整shared_buffers打基础

版本为 20devel (https://github.com/postgres/postgres/commit/6cda9dfc4f9764b6957e4e214193936ffb92906b)

PG把共享内存注册基础设施做了优化调整,本篇做一下分析。

复制代码
2024-10-18  | PG18devel 开发期 | v1 PoC
    |
    +-- 首次提出不重启调整 shared_buffers
    |   主要验证在线增大,尚未完整处理缩小、跨平台等问题
    |   状态:未合入
    |
2026-04-04  | PG19devel
    |
    +-- 整理共享哈希表的内存布局
    |   固定容量;表头、目录、元素放进同一连续区域
    |   提交:999e9ebb51c9 / 9fe9ecd516b0
    |   状态:已合入,属于 PG19
    |
2026-04-06  | PG19devel
    |
    +-- 统一共享内存注册、请求、初始化、附着接口
    |   提交:283e823f9dcb
    |
    +-- 增加内置子系统注册清单
    |   提交:1fc2e9fbc0a3
    |
    +-- 增加对齐选项,迁移 buffer manager 和其他子系统
    |   提交:dacfe81a0de5 / a4b6139dcceb / 9b5acad3f40f
    |
    +-- 上述基础设施均已合入 PG19
    |   但此时不能在线调整 shared_buffers
    |
2026-06-12  | 当时上游为 PG19devel | Databricks 公开补丁
    |
    +-- Haoyu 提交在线扩缩容代码和设计说明
    |   来自基于 PG18 的内部开发分支,公开补丁已移植到当时 master
    |   状态:内部已合入;不是上游已合入
    |
2026-06-29  | master 从 PG19devel 切换为 PG20devel
    |
    +-- 此后 master 上的新提交属于 PG20 开发线
    |   需要修复 PG19 时,另行回补到 REL_19_STABLE
    |
2026-07-24  | PG20devel | v20260724
    |
    +-- 在线扩缩容方案接入 PG19 已合入的新框架
    |   吸收 Databricks 的部分思路和代码
    |   增加可调整大小的共享结构、buffer 扩缩容及测试
    |   状态:补丁评审中,未合入
    |
2026-08-26  | PG20devel + 回补 PG19
    +-- 修复单用户模式下的共享内存回调注册
    |
2026-08-28  | PG20devel + 回补 PG19
    +-- 注册失败后清理请求、恢复状态机
    +-- 增加 initialized 标记,拒绝附着半初始化对象
    |
2026-09-18  | PG20devel + 回补 PG19
    +-- 修复未知大小附着到不存在的对象;增加溢出检查
    |
2026-09-24  | PG20devel + 回补 PG19
    +-- 修复旧接口在单用户模式下重复附着失败
    |
2026-09-30  | PG20devel + 回补 PG19
    +-- 允许单用户模式附着已有的未知大小对象
    |
2026-10-10  | PG20devel + 回补 PG19
    |
    +-- 将进程私有的 writeback context 初始化移出共享内存回调
    |   提交:15a947f30726
    |
2026-10-20  | PG20devel 工作线 | v20261017,共 23 个补丁
    |
    +-- 在线扩缩容继续处理并行 worker、并发读取、性能、失败回滚
        状态:整体功能仍未合入上游

1. 改了什么

共享内存对象的生命周期拆成了四部分

text 复制代码
注册回调 -> 声明对象需求 -> 分配并绑定地址 -> 初始化内容 / 附着已有对象

以前子系统自己计算大小再调用分配函数,根据 found 判断是否初始化。

现在,子系统声明"对象名称、大小、对齐、指针接收位置",框架统一分配,再调用适当的回调。

项目 旧流程 新流程
对象大小 XXXShmemSize() 与分配代码分别维护 ShmemRequestStruct() 同时声明名称和大小
内置子系统接入 修改 ipci.c 的大小计算和初始化清单 在 subsystemlist.h 登记回调
扩展接入 串接 shmem_request_hook、shmem_startup_hook _PG_init() 注册 ShmemCallbacks
分配与初始化 常放在同一个 XXXShmemInit() 中 框架分配,init_fn 初始化内容
附着 再跑初始化入口,由 found、进程类型分支判断 框架恢复指针,必要时调用 attach_fn
启动后注册 调用者自行处理对象查找、初始化同步 显式允许后,由框架串行处理一组请求

没有改变的边界:

  • 这里管理的是主共享内存段里的命名对象,不是每个对象各建一个 OS 共享内存段。
  • 主共享内存仍须映射到各进程的相同地址,结构中可以使用普通指针。
  • 底层仍从固定大小的共享内存段中划出空间,已分配区域不能单独释放。
  • 它不是 DSM/DSA,也没有让 shared_buffers 自动变成可在线修改的参数。

2. 旧流程:计算和初始化两份清单

来源:旧 ipci.c、旧 buf_init.c。

text 复制代码
PostmasterMain
  |
  +-- 加载 shared_preload_libraries
  |     `-- 扩展安装 request/startup hook
  |
  +-- process_shmem_requests()
  |     `-- 旧 request hook -> RequestAddinShmemSpace(bytes)
  |
  `-- CreateSharedMemoryAndSemaphores()
        +-- CalculateShmemSize()
        |     +-- BufferManagerShmemSize()
        |     +-- LockManagerShmemSize()
        |     +-- ...其他 XXXShmemSize()
        |     `-- 加上扩展请求及预留空间
        |
        +-- PGSharedMemoryCreate()
        +-- InitShmemAllocator()
        +-- CreateOrAttachShmemStructs()
        |     +-- LWLockShmemInit()
        |     +-- ...其他 XXXShmemInit()
        |     `-- BufferManagerShmemInit()
        |           +-- ShmemInitStruct(name, size, &found)
        |           +-- found=false: 初始化内容
        |           `-- found=true: 复用已有内容
        |
        +-- dsm_postmaster_startup()
        `-- 旧 shmem_startup_hook

以 buffer pool 为例,大小计算在 BufferManagerShmemSize():

c 复制代码
/* 旧代码:估算时手动留出 IO 对齐余量。 */
size = add_size(size, PG_IO_ALIGN_SIZE);
size = add_size(size, mul_size(NBuffers, BLCKSZ));

实际定位和分配却在 BufferManagerShmemInit():

c 复制代码
/* 旧代码:再次计算大小,分配后再手动对齐返回地址。 */
BufferBlocks = (char *)
    TYPEALIGN(PG_IO_ALIGN_SIZE,
              ShmemInitStruct("Buffer Blocks",
                              NBuffers * (Size) BLCKSZ + PG_IO_ALIGN_SIZE,
                              &foundBufs));

问题不在于不能运行,而在于同一对象的信息分散:

  1. 大小公式、对齐余量要在两个位置保持一致。
  2. 增加一个内置子系统,要改中央清单。
  3. EXEC_BACKEND 子进程也运行 CreateOrAttachShmemStructs(),初始化函数同时承担"创建"和"附着"两种职责。
  4. 一次初始化多个对象时,调用者还要保证"全部找到"或"全部新建";旧 buffer manager 就显式检查四个 found 标志。

3. 新接口:回调注册与对象请求是两层

来源:shmem.h。

c 复制代码
typedef struct ShmemCallbacks
{
    int flags;                         /* 是否允许启动后注册 */
    ShmemRequestCallback request_fn;    /* 声明需求,不在这里操作共享内容 */
    ShmemInitCallback init_fn;          /* 新对象的业务初始化 */
    ShmemAttachCallback attach_fn;      /* 已有对象附着后的进程私有设置 */
    void *opaque_arg;                  /* 原样传给上述回调 */
} ShmemCallbacks;

一个子系统可以请求多个对象。每个普通对象用下面的描述符:

c 复制代码
typedef struct ShmemStructOpts
{
    const char *name;       /* 在 ShmemIndex 中查找对象的名字 */
    ssize_t size;          /* 字节数;未知大小仅用于附着已有对象 */
    size_t alignment;      /* 可选,至少按 cache line 对齐 */
    void **ptr;            /* 接收对象地址的进程私有指针变量 */
} ShmemStructOpts;

RegisterShmemCallbacks() 不是分配函数。启动阶段,它把回调指针放入进程私有的 registered_shmem_callbacks 链表,留待后续阶段执行。

调用 request_fn 后,ShmemRequestStruct() 才把对象需求放入 pending_shmem_requests。实现会检查当前阶段、名称、大小、对齐和本轮请求中是否重名。

这里有三个不同层次:

数据 所在位置 用途
registered_shmem_callbacks 进程私有内存 子系统的回调清单,崩溃重建时仍可复用
pending_shmem_requests TopMemoryContext 当前这轮待分配或待附着的对象描述
ShmemIndex 主共享内存;本地另有 HTAB 句柄 保存已经分配的对象名称、地址、大小和初始化状态

生命周期约束:

  • 回调结构只保存指针,通常声明为 static const,不能传一个即将失效的栈变量。
  • 对象 options 会复制到 TopMemoryContext,但 .name 只复制字符串指针,名字也须长期有效。
  • .ptr 是"指针变量的地址",不是共享内存地址。框架完成分配或查找后,才给 *ptr 赋值。

4. 新启动流程

来源:postmaster.c、ipci.c。

text 复制代码
PostmasterMain
  |
  +-- RegisterBuiltinShmemCallbacks()
  |     `-- 按 subsystemlist.h 顺序注册内置子系统
  |
  +-- process_shared_preload_libraries()
  |     `-- 扩展 _PG_init() -> RegisterShmemCallbacks()
  |
  +-- InitializeMaxBackends() / InitializeFastPathLocks()
  |
  +-- process_shmem_requests()             [保留旧扩展 hook]
  |     `-- 可能请求额外内存和 named LWLocks
  |
  +-- ShmemCallRequestCallbacks()
  |     `-- 逐个 request_fn
  |           `-- ShmemRequestStruct/Hash -> pending_shmem_requests
  |
  +-- InitializeShmemGUCs()                [用已收集的需求计算展示值]
  |
  `-- CreateSharedMemoryAndSemaphores()
        +-- CalculateShmemSize()
        +-- PGSharedMemoryCreate()         [创建主共享内存段]
        +-- InitShmemAllocator()           [分配器与 ShmemIndex 自举]
        +-- ShmemInitRequested()
        |     1. 分配全部请求,建立索引,设置各对象指针
        |     2. 按注册顺序调用全部 init_fn
        |     3. 标记 initialized,释放本轮请求描述
        +-- dsm_postmaster_startup()
        `-- 旧 shmem_startup_hook

旧 request hook 必须先于新 request callbacks 执行。原因是旧扩展可能通过 RequestNamedLWLockTranche() 增加锁需求,而新的 LWLock 请求回调需要据此计算最终大小。

4.1 内置清单不是自动发现

subsystemlist.h 使用 X-macro:同一清单既能生成 extern 声明,也能生成注册调用。ipci.c 中的展开方式是:

c 复制代码
/* 每一项展开成一次 RegisterShmemCallbacks()。 */
#define PG_SHMEM_SUBSYSTEM(subsystem_callbacks) \
    RegisterShmemCallbacks(&(subsystem_callbacks));

#include "storage/subsystemlist.h"

#undef PG_SHMEM_SUBSYSTEM

顺序仍然有意义。LWLock 注册在最前面,保证其他初始化回调可以使用它。新框架没有自动推导子系统依赖关系。

4.2 大小计算统一,但没有换掉底层分配器

ShmemGetRequestedSize() 汇总:

text 复制代码
ShmemIndex 所需空间(请求项数 + 128 个预留项)
  + 每个请求的对齐填充
  + 每个请求的 size

CalculateShmemSize() 再加上原有的 100000 字节余量、旧扩展额外请求,并按 8KB 边界补齐。这仍是带余量的容量计算,不是运行时可无限增长的内存池。

实际分配仍由 ShmemAllocRaw() 推进 free_offset,函数内的关键片段如下:

c 复制代码
SpinLockAcquire(&ShmemAllocator->shmem_lock);

rawStart = ShmemAllocator->free_offset;
newStart = TYPEALIGN(alignment, rawStart);

/* 对齐后检查加法溢出和共享段边界,成功才推进游标。 */
if (!pg_add_size_overflow(newStart, size, &newFree) &&
    newFree <= ShmemSegHdr->totalsize)
{
    newSpace = (char *) ShmemBase + newStart;
    ShmemAllocator->free_offset = newFree;
}
else
    newSpace = NULL;

SpinLockRelease(&ShmemAllocator->shmem_lock);

锁的职责不要混淆:

  • shmem_lock 自旋锁保护分配游标。
  • ShmemIndexLock 保护命名目录,以及后文的启动后初始化过程。
  • 对象投入使用后的读写,仍由各子系统自己的锁、原子变量负责。

4.3 先绑定所有指针,再调用初始化回调

ShmemInitRequested() 的主体:

c 复制代码
/* 第一轮:分配、登记对象,并设置调用者的指针变量。 */
foreach_ptr(ShmemRequest, request, pending_shmem_requests)
{
    InitShmemIndexEntry(request);
}

/* 第二轮:执行子系统的内容初始化。 */
foreach_ptr(const ShmemCallbacks, callbacks, registered_shmem_callbacks)
{
    if (callbacks->init_fn)
        callbacks->init_fn(callbacks->opaque_arg);
}

/* 全部初始化成功后,才标记完成。这里释放的是请求描述,不是共享对象。 */
foreach_ptr(ShmemRequest, request, pending_shmem_requests)
{
    request->index_entry->initialized = true;
    pfree(request->options);
}
list_free_deep(pending_shmem_requests);
pending_shmem_requests = NIL;

shmem_request_state = SRS_DONE;

InitShmemIndexEntry() 会先建立目录项,再划出空间,记录 location、size、allocated_size,把 initialized 设为 false。普通结构直接写入 *ptr;HASH 和 SLRU 则执行各自的基础初始化。

因此,init_fn 运行时,对象地址已经就绪;但这不表示后面其他子系统的 init_fn 也已经执行完。跨子系统的内容依赖仍受注册顺序约束。

5. Buffer manager 的具体变化

来源:buf_init.c、buf_table.c。

最新代码里的回调定义:

c 复制代码
const ShmemCallbacks BufferManagerShmemCallbacks = {
    .request_fn = BufferManagerShmemRequest,
    .init_fn = BufferManagerShmemInit,
};

BufferManagerShmemRequest() 一次声明四块区域:

名称 大小 接收指针
Buffer Descriptors NBuffers * sizeof(BufferDescPadded) BufferDescriptors
Buffer Blocks NBuffers * BLCKSZ BufferBlocks
Buffer IO Condition Variables NBuffers * sizeof(ConditionVariableMinimallyPadded) BufferIOCVArray
Checkpoint BufferIds NBuffers * sizeof(CkptSortItem) CkptBufferIds

其中数据页的请求变成:

c 复制代码
ShmemRequestStruct(.name = "Buffer Blocks",
                   .size = NBuffers * (Size) BLCKSZ,
                   .alignment = PG_IO_ALIGN_SIZE, /* 不再由调用者多申请再挪指针 */
                   .ptr = (void **) &BufferBlocks,
    );

与旧代码相比:

  • 删除独立的 BufferManagerShmemSize(),不再维护第二份大小公式。
  • 删除四个 found 标志和"创建还是附着"的分支。
  • BufferManagerShmemInit() 只初始化 buffer descriptor、原子状态、等待队列和 IO 条件变量。
  • 替换策略和 buffer 映射哈希表分别作为 StrategyCtlShmemCallbacks、BufTableShmemCallbacks 登记,不再由 buffer 初始化函数间接调用。

共享哈希表:数据共享,操作句柄私有

buffer 映射表的实际请求:

c 复制代码
size = NBuffers + NUM_BUFFER_PARTITIONS;

ShmemRequestHash(.name = "Shared Buffer Lookup Table",
                 .nelems = size,
                 .ptr = &SharedBufHash, /* 接收本进程的 HTAB 句柄 */
                 .hash_info.keysize = sizeof(BufferTag),
                 .hash_info.entrysize = sizeof(BufferLookupEnt),
                 .hash_info.num_partitions = NUM_BUFFER_PARTITIONS,
                 .hash_flags = HASH_ELEM | HASH_BLOBS | HASH_PARTITION | HASH_FIXED_SIZE,
    );

额外的 NUM_BUFFER_PARTITIONS 是为 buffer 映射更新中"先插入新项、再删除旧项"预留容量,不是可动态扩容的空间。

shmem_hash.c 先用 hash_estimate_size() 计算整个哈希表的大小,再以 SHMEM_KIND_HASH 进入同一请求队列:

text 复制代码
ShmemRequestHash()
  -> 计算 HASHHDR、目录、元素等所需的连续区域
  -> ShmemRequestInternal(..., SHMEM_KIND_HASH)

首次分配 -> shmem_hash_init()   -> 创建共享内容和本地 HTAB
已有对象 -> shmem_hash_attach() -> HASH_ATTACH,重建本地 HTAB

不能把 HTAB * 当作所有进程共同使用的控制对象。共享的是表头、目录和元素等数据;HTAB 操作句柄是进程私有的。当前实现统一使用 HASH_FIXED_SIZE,删除元素可以复用槽位,但不会向主共享段追加容量。

6. "附着"具体做什么

6.1 普通 fork

text 复制代码
postmaster 完成共享对象初始化
  -> fork()
  -> 子进程继承共享映射
  -> 子进程也继承自己的指针变量和 HTAB 句柄副本
  -> 不重跑 request_fn、init_fn、attach_fn

最后一条只针对启动时已登记的对象。后续按需加载的扩展,仍可能走启动后附着路径。

6.2 EXEC_BACKEND

来源:launch_backend.c、proc.c、ShmemAttachRequested()。

text 复制代码
EXEC_BACKEND 子进程启动
  +-- PGSharedMemoryReAttach()             [先把共享段映射到相同地址]
  +-- 读取 GUC
  +-- RegisterBuiltinShmemCallbacks()
  +-- 重新加载 shared_preload_libraries
  |     `-- 重新取得本进程有效的回调函数地址
  +-- InitShmemAllocator()                 [连接已有分配器及 ShmemIndex]
  +-- ShmemCallRequestCallbacks()          [重建对象请求描述,不分配共享数据]
  |
  `-- InitProcess() 等完成 MyProc / LWLock 本地状态
        `-- AttachSharedMemoryStructs()
              `-- ShmemAttachRequested()
                    +-- 持 ShmemIndexLock 共享锁
                    +-- 按名称查找,检查 initialized 和大小
                    +-- 设置 *ptr / 重建 HTAB 等本地句柄
                    +-- 调用 attach_fn
                    `-- 释放锁,进入 DONE

attach_fn 不是再初始化一遍共享数据。它用于恢复不能通过 exec 继承的进程私有状态。普通共享结构只需要框架替它设置指针,所以可以没有 attach_fn。

当前 buffer manager 就没有 attach_fn。早期迁移版本还有一个,用于初始化纯进程私有的 BackendWritebackContext;2026-10-10 的补丁把这件事移到了 InitBufferManagerAccess(),避免把普通后端初始化混进共享内存生命周期。

6.3 postmaster 崩溃重建

普通子进程退出不会重新初始化共享对象。发生需要重建共享内存的崩溃恢复时,postmaster.c 则执行:

text 复制代码
清理旧共享内存
  -> ResetShmemAllocator()
       清空分配器、目录指针和本轮请求,保留 registered_shmem_callbacks
  -> ShmemCallRequestCallbacks()
  -> CreateSharedMemoryAndSemaphores()

保留回调清单是为了重新执行需求声明,不必重新加载全部预加载扩展。

7. 启动后注册:允许按需使用,不保证还有空间

RegisterShmemCallbacks() 在 SRS_DONE 阶段只接受设置了 SHMEM_CALLBACKS_ALLOW_AFTER_STARTUP 的调用,并立即执行回调。

text 复制代码
RegisterShmemCallbacks(ALLOW_AFTER_STARTUP)
  -> request_fn 收集这一组请求
  -> ProcessShmemRequestsAfterStartup()
       持 ShmemIndexLock 排他锁
       |
       +-- 所有对象都存在且初始化完成 -> attach 全部对象 -> attach_fn
       +-- 所有对象都不存在           -> allocate 全部对象 -> init_fn
       `-- 只存在一部分 / 有半初始化项 -> 报错
       |
       标记 initialized,释放锁
  -> 清理请求描述,恢复 SRS_DONE

一组请求必须"全部存在"或"全部不存在",否则框架无法确定应该调用哪个回调。init_fn、attach_fn 都在这把排他锁内执行,防止两个后端同时初始化。回调不要再调用会重入取得该锁的共享内存接口。

启动阶段没有并发后端,批量初始化不需要这把锁;启动后的首次加载有并发,不能照搬启动阶段的无锁假设。

失败处理分两层

  1. 进程私有状态要恢复。 CallShmemCallbacksAfterStartup() 用 PG_TRY / PG_FINALLY 清理请求描述,并把状态恢复到 SRS_DONE,不让下一次调用处理残留请求。
  2. 共享对象不会随之回滚。 对象分配成功、但初始化中途失败时,目录项留下 initialized=false。后续附着被拒绝,通常要重启才能清理,不是再次注册就能重试。

这一点是读初版补丁容易漏掉的:清理请求链表,不等于释放共享内存。

SHMEM_ATTACH_UNKNOWN_SIZE 也不是动态大小申请。它只用于附着一个已存在的对象,并跳过大小相等检查;对象不存在就报错,不能拿 -1 当作待分配大小。

旧扩展的兼容路径仍在

ShmemInitStruct() 现在包装上述查找和分配逻辑,仍返回 found。新建时它立即把目录项标为完成,调用者再做自己的内容初始化。因此,旧扩展仍须保留原有的初始化同步,不能把这个包装当成新回调模式的完整替代。

实际迁移可参考 pg_stat_statements.c:在 _PG_init() 注册回调,在 pgss_shmem_request() 请求状态结构及哈希表,在 pgss_shmem_init() 初始化锁、状态并恢复持久化统计数据。

8. 补丁如何演进

8.1 基础改造与迁移

主框架作者为 Heikki Linnakangas,283e823f9dcb 的共同作者为 Ashutosh Bapat。主体在 PG19 开发期合入,REL_19_RC1 已包含这些提交。

时间 提交 实际变化
2026-04-04 999e9ebb51c9、9fe9ecd516b0 先将共享哈希表固定容量化,再将表头、目录和元素收拢为一个连续区域;对象目录和内存统计才能反映整张表的占用。这两项已包含在本文旧流程基准中。
2026-04-06 283e823f9dcb 引入请求、初始化、附着回调;统一对象描述;保留旧函数兼容层。不是这个提交单独完成所有子系统迁移。
2026-04-06 1fc2e9fbc0a3 引入内置子系统清单,供随后迁移逐步接入。
2026-04-06 dacfe81a0de5 增加显式 alignment,消除 buffer pool 等调用者手动多分配再对齐的做法。
2026-04-06 a4b6139dcceb 迁移 buffer manager,将替换策略、映射哈希表分别登记。
2026-04-06 9b5acad3f40f 完成剩余内置子系统迁移;旧接口仍为扩展保留。LWLock、SLRU、AIO 已在同系列前面的提交中迁移。
2026-04-06 ed71d7356e3b 修复请求描述的内存泄漏:清理请求时也要释放 request->options。不是释放共享对象。

8.2 初版之后的重要修正

时间 提交 为什么需要
2026-07-07 6f7199a1245c 明确启动后回调持有 ShmemIndexLock,避免扩展误判初始化时的锁环境。
2026-08-26 edc095a26529 正确识别单用户模式下的启动后注册;postmaster 启动后不能进入需要拿锁的注册路径。
2026-08-28 c54b6979e033 注册或回调报错后恢复状态机,清理残留请求,避免下一次注册访问已释放的描述或走错阶段。
2026-08-28 07fdee7c8a8b 增加 initialized 标志,区分"分配成功"和"初始化完成";拒绝使用失败后留下的半成品。
2026-09-18 311df1dc0392 禁止为不存在的对象使用未知大小标记;同时在分配器增加整数加法溢出检查,避免把 -1 当容量。
2026-09-24 4e277b424a53 修复旧 ShmemInitStruct() 在单用户模式重复附着失败:是否先查找已有对象不应由 IsUnderPostmaster 决定。
2026-09-30 c712b0d49b97 单用户模式也可附着未知大小对象。判断依据改为共享内存是否已初始化,而不是进程是否为 postmaster 子进程。
2026-10-10 15a947f30726 将纯进程私有的 writeback context 初始化移出 buffer manager 的共享内存回调;最新代码因此不再需要对应的 attach 回调。

这些修正主要解决生命周期边界,不是在线扩缩容。读代码时应以当前状态机和 initialized 检查为准,不能只看 4 月的初始补丁。

9. 与动态 shared_buffers 的关系

已经合入的是"对象可声明、可统一管理"的框架,不是"对象可改变大小"的框架:

  • ShmemStructOpts 只有当前请求大小,没有最大容量或 resize 回调。
  • AttachShmemIndexEntry() 默认要求请求大小与既有大小一致;跳过检查不会改变对象大小。
  • ShmemAllocRaw() 只在现有共享段内推进游标,没有回收接口。
  • 当前源码没有 max_shared_buffers、pg_resize_shared_buffers();shared_buffers 仍为 PGC_POSTMASTER 参数。

这套注册机制让后续扩缩容更容易接入:对象大小、位置及生命周期集中可见。但地址空间预留、物理页回收、跨进程协调,以及 buffer pin、AIO、旧 buffer 编号等问题,仍需要另外的实现。

databricks再26年6月给了一份实现,但是没有复用共享内存基础设施的修改,社区没采纳。

https://www.postgresql.org/message-id/CAM1e6U5XDwKYZo6Jj3yD3xpCB4qkhRSQn8upauHt%3DWhEbK9VZA%40mail.gmail.com

10. 其他

相关推荐
数据库小学妹1 小时前
AI Agent连数据库怎么控权限?只读账号的4个缺口
数据库·人工智能·安全·数据库权限·审计·ai数据库·只读账号
瑞克-桑切斯1 小时前
6、MySQL 总结
数据库
凤凰院凶涛QAQ1 小时前
《MySQL内核拆解:从SQL到InnoDB》(三) : 数据库设计 & 聚合查询 & 分组查询
数据库·sql·mysql
Decay__2 小时前
01_MySQL 体系架构全解 —— 连接层、SQL 层、存储引擎层与 InnoDB 深入
运维·数据库·sql·mysql·架构
Linux_cy3 小时前
nginx 502 排查手册:九种原因分类、定位命令与检查顺序
linux·运维·服务器·网络·数据库·nginx·ssh
在多学一点3 小时前
MySQL_DDL的基本命令
数据库·mysql·adb
是程序喵呀3 小时前
Redis实现持久化
数据库·redis·缓存
余槐i4 小时前
PostgreSQL 加索引后查询反而更慢:优化器弃用索引的 3 种情形
spring boot·redis·mysql·postgresql·事务
z06.4 小时前
MySQL存储引擎
数据库·mysql