Heikki 出手,重构 PG 共享内存子系统
Heikki 出手重构 PG 共享内存子系统:统一注册机制
本文档解读 Heikki Linnakangas 提交的共享内存子系统重构,该重构引入了统一的共享内存区域注册机制,使 pg_shmem_allocations 视图更加完整,便于 DBA 诊断内存使用问题。
相关 Commit
1bdbb211bbf3 | |
1fc2e9fbc0a3d | |
283e823f9dcb | |
9b5acad3f40fa | |
a4b6139dcceb4 | |
a006bc7b1699d | |
d4885af3d6532 |
核心价值
可观测性: pg_shmem_allocations视图现在可见 ShmemIndex统一接口:所有子系统使用相同的共享内存分配 API 更好的诊断:便于识别内存泄漏或异常内存使用
原理详解
1. 共享内存索引 (ShmemIndex)
1.1 ShmemIndex 定义 (src/backend/storage/ipc/shmem.c line 250-272)
ShmemIndex 是一个全局的共享内存区域目录,本身也存储在共享内存中:
/*
* ShmemIndex 是一个哈希表
* 用于快速查找共享内存中的各个区域
*/
static HTAB *ShmemIndex;/* 哈希表条目结构 */
typedefstructShmemIndexEnt
{
char key[SHMEM_INDEX_KEYSIZE]; /* 字符串名称(最多48字符)*/
void *location; /* 共享内存中的位置 */
Size size; /* 请求的字节数 */
Size allocated_size; /* 实际分配的字节数 */
} ShmemIndexEnt;
1.2 访问方式
ShmemIndex 通过内部函数访问,不对外公开。子系统通过 ShmemRequestStruct() 和 ShmemRequestHash() 注册后,由 ShmemInitStruct() 分配和返回指针。
2. 共享内存请求机制
2.1 ShmemRequestStructWithOpts (src/backend/storage/ipc/shmem.c line 316-325)
子系统调用此函数注册其共享内存需求:
/*
* 请求一个命名的共享内存区域
* 通常在 postmaster 启动早期调用
*/
void
ShmemRequestStructWithOpts(const ShmemStructOpts *options)
{
ShmemStructOpts *options_copy; options_copy = MemoryContextAlloc(TopMemoryContext,
sizeof(ShmemStructOpts));
memcpy(options_copy, options, sizeof(ShmemStructOpts));
ShmemRequestInternal(options_copy, SHMEM_KIND_STRUCT);
}
2.2 ShmemRequestHash (src/backend/storage/ipc/shmem.c)
用于请求共享内存哈希表:
ShmemRequestHash(.name = "LOCK hash",
.nelems = max_table_size,
.ptr = &LockMethodLockHash,
.hash_info.keysize = sizeof(LOCKTAG),
.hash_info.entrysize = sizeof(LOCK),
.hash_info.num_partitions = NUM_LOCK_PARTITIONS,
.hash_flags = HASH_ELEM | HASH_BLOBS | HASH_PARTITION,
);
3. pg_shmem_allocations 视图增强
3.1 视图定义
pg_shmem_allocations 视图显示所有共享内存分配:
CREATEVIEW pg_shmem_allocations AS
SELECT * FROM pg_get_shmem_allocations();
pg_shmem_allocations 视图返回 4 列:
name: 分配名称 off: 偏移量 size: 请求的大小 allocated_size: 实际分配的大小
3.2 新增可见内容
现在 ShmemIndex 本身也可见于视图,便于诊断:
可见项:
- ShmemIndex # 共享内存索引本身
- Buffer Descriptors
- WAL Buffers
- Lock Data
- ...
4. ShmemStructOpts 结构
子系统使用 ShmemStructOpts 结构指定共享内存需求:
typedefstructShmemStructOpts
{
constchar *name; /* 子系统名称 */
Size size; /* 请求的大小 */
void **ptr; /* 输出:分配的指针 */
Size alignment; /* 对齐要求 */
} ShmemStructOpts;
5. 典型子系统转换示例
5.1 Buffer Manager 请求 (src/backend/storage/buffer/bufmgr.c)
/* 在 _PG_init 中注册共享内存需求 */
void
_PG_init(void)
{
ShmemRequestStruct(.name = "Buffer Descriptors",
.size = BufferDescsSize(),
.ptr = (void **) &BufferDescriptors,
.alignment = 16);
}
5.2 LWLock 请求 (src/backend/storage/lmgr/lwlock.c)
/* 请求主 LWLock 数组 */
void
LWLockShmemRequest(void)
{
Size size;/* 计算主 LWLock 数组大小 */
if (!IsUnderPostmaster)
{
int num_main_array_locks = NUM_FIXED_LWLOCKS + NumLWLocksForNamedTranches();
size = num_main_array_locks * sizeof(LWLockPadded);
}
else
size = SHMEM_ATTACH_UNKNOWN_SIZE;
ShmemRequestStruct(.name = "Main LWLock array",
.size = size,
.ptr = (void **) &MainLWLockArray);
}
使用实践
1. 查看共享内存使用
-- 查看所有共享内存分配
SELECT
name,
off,
size,
allocated_size
FROM pg_shmem_allocations
ORDERBYsizeDESC;-- 按名称分组统计
SELECT
name,
count(*) as instances,
sum(size) as total_size
FROM pg_shmem_allocations
GROUPBYname
ORDERBY total_size DESC;
2. 诊断内存问题
2.1 检查 ShmemIndex
-- 查看 ShmemIndex 内容(新增)
SELECT * FROM pg_shmem_allocations
WHEREname = 'ShmemIndex';
2.2 检查 Buffer Manager
-- 查看缓冲区内存使用
SELECT
name,
size,
allocated_size
FROM pg_shmem_allocations
WHEREnameLIKE'Buffer%';
2.3 检查 Lock Manager
-- 查看锁管理器内存
SELECT
name,
size
FROM pg_shmem_allocations
WHEREnameLIKE'%Lock%';
3. 监控内存变化
-- 创建历史记录表
CREATETABLE shmem_snapshots (
idserial PRIMARY KEY,
recorded_at timestamptz DEFAULTnow(),
nametext,
instanceint,
sizebigint
);-- 记录快照
INSERTINTO shmem_snapshots(name, instance, size)
SELECTname, instance, size
FROM pg_shmem_allocations;
-- 对比快照
SELECT
s1.name,
s1.size as old_size,
s2.size as new_size,
s2.size - s1.size as diff
FROM shmem_snapshots s1
JOIN (
SELECTname, instance, size
FROM pg_shmem_allocations
) s2 ON s1.name = s2.name AND s1.instance = s2.instance
WHERE s2.size != s1.size
ORDERBYabs(s2.size - s1.size) DESC;
4. 诊断内存泄漏
-- 检查是否有异常的内存增长
SELECT
name,
sum(size) as total_size,
sum(allocated_size) as total_allocated_size,
count(*) as allocation_count
FROM pg_shmem_allocations
GROUPBYname
ORDERBY total_size DESC;-- 如果某个子系统内存持续增长,可能是泄漏
5. 配置调优参考
根据 pg_shmem_allocations 的输出,可以调整:
# 共享缓冲区
shared_buffers = 8GB# 工作内存
work_mem = 64MB
# 维护工作内存
maintenance_work_mem = 2GB
# 锁等待超时
max_locks_per_transaction = 128
6. 常见问题诊断
6.1 共享内存不足
-- 查看总共享内存使用
SELECT
sum(size) as total_shmem_used,
pg_size_pretty(sum(size))
FROM pg_shmem_allocations;-- 查看各个子系统的内存使用
SELECT
name,
sum(size) as requested_size,
sum(allocated_size) as allocated_size
FROM pg_shmem_allocations
GROUPBYname
ORDERBY allocated_size DESC;
6.2 内存碎片化
-- 检查分配模式
SELECT
name,
off,
size
FROM pg_shmem_allocations
WHEREname = 'Buffer Descriptors'
ORDERBYoff;
总结
共享内存子系统重构为 DBA 提供了更好的可观测性。
关键要点:
pg_shmem_allocations 增强:ShmemIndex 现在可见 统一 API:所有子系统使用相同的注册和分配接口 便于诊断:可以更好地识别内存问题 向后兼容:旧的 API 仍然可用
对 DBA 的价值:
更完整的共享内存视图 便于诊断内存问题 支持内存使用历史跟踪 帮助配置调优