增加max_replication_slots参数会带来多少内存的开销?

admin 1月前 65

我们知道max_replication_slots是一个重要的参数,它代表复制槽的最大个数。这个参数的修改必须重启数据库实例才行。在实践中,我们必须把这个参数的值设置的足够大,才能避免未来重启数据库。它的缺省值是10,对于真实的生产环境是不够的,建议把它调整为32或者64。

问题来了,如果增加这个参数的值,会带来什么开销呢?我们直接撸源代码。

在src/backend/postmaster/postmaster.c中有一个函数是在启动数据库实例时创建共享内存。这个内存一旦创建好了,就不能改变,除非重启数据库:

	/*
	 * Set up shared memory and semaphores.
	 *
	 * Note: if using SysV shmem and/or semas, each postmaster startup will
	 * normally choose the same IPC keys.  This helps ensure that we will
	 * clean up dead IPC objects if the postmaster crashes and is restarted.
	 */
	CreateSharedMemoryAndSemaphores();

我们顺藤摸瓜,找到这个函数的定义在src/backend/storage/ipc/ipci.c中:

/*
 * CreateSharedMemoryAndSemaphores
 *		Creates and initializes shared memory and semaphores.
 */
void
CreateSharedMemoryAndSemaphores(void)
{
	PGShmemHeader *shim;
	PGShmemHeader *seghdr;
	Size		size;
	int			numSemas;

	Assert(!IsUnderPostmaster);

	/* Compute the size of the shared-memory block */
	size = CalculateShmemSize(&numSemas);
	elog(DEBUG3, "invoking IpcMemoryCreate(size=%zu)", size);

	/*
	 * Create the shmem segment
	 */
	seghdr = PGSharedMemoryCreate(size, &shim);

我们可以看到,CalculateShmemSize()函数计算整个共享内存的尺寸,我们继续追踪这个函数的实现:

size = add_size(size, ReplicationSlotsShmemSize());

函数ReplicationSlotsShmemSize()计算复制槽的大小,我们查看这个函数,在src/backend/replication/slot.c中:

/*
 * Report shared-memory space needed by ReplicationSlotsShmemInit.
 */
Size
ReplicationSlotsShmemSize(void)
{
	Size		size = 0;

	if (max_replication_slots == 0)
		return size;

	size = offsetof(ReplicationSlotCtlData, replication_slots);
	size = add_size(size,
					mul_size(max_replication_slots, sizeof(ReplicationSlot)));

	return size;
}

在上述函数的逻辑中,我们看到,最终复制槽的尺寸 size = a + m * n,其中

a = offsetof(ReplicationSlotCtlData, replication_slots);

m = max_replication_slots

n = sizeof(ReplicationSlot)

我们写一个小程序,把这些数据结构提取出来,就可以容易知道如果增加max_replication_slots会带来多少字节的共享内存的开销了。假设max_replication_slots的当前值是x, 我们把它增加到y,增加的共享内存尺寸就是:

(y - x) * sizeof(ReplicationSlot)

所以关键是看结构体ReplicationSlot占多少个字节。如何查看这个结构体占据多少字节呢?你可以使用笨方法,把它里面的各个字段占多少个字节统计一下即可。下面我介绍一个简单的方法,直接修改PG的源码,利用GDB来下断点,查看它的大小。

wget https://ftp.postgresql.org/pub/source/v18beta1/postgresql-18beta1.tar.bz2  ### 下载最新的源代码包
tar jxvf postgresql-18beta1.tar.bz2 ### 解压缩
cd postgresql-18beta1 ### 进入源代码包中

vi src/backend/postmaster/postmaster.c  ### 编辑这个文件,加入如下一行

#include "replication/slotsync.h"
#include "replication/slot.h"  //这一行是新加的
#include "replication/walsender.h"

然后在下面增加几行
/*
 * Postmaster main entry point
 */
void
PostmasterMain(int argc, char *argv[])
{
        int                     opt;
        int                     status;
        char       *userDoption = NULL;
        bool            listen_addr_saved = false;
        char       *output_config_variable = NULL;

        size_t rs = sizeof(ReplicationSlot);  // 这一行是我加的

        if(rs == 0)   // 也是我加的
                return;  // 也是我加的,避免编译器抱怨。

        InitProcessGlobals();


### 然后三部曲编译源代码
./configure --prefix=/home/postgres/pg18b1 --enable-debug --enable-cassert CFLAGS="-O0" --without-icu
make
make install
### 使用gdb调试/home/postgres/pg18b1/bin/postgres这个程序:
gdb -tui /home/postgres/pg18b1/bin/postgres
b PostmasterMain  ## 在这个函数下断点
r  ## 执行到断点,然后单步执行
s ## 单步执行,获得rs的值
p rs ## 打印rs的值

结果看到rs的值为280

通过gdb,我们知道结构体ReplicationSlot的大小为280个字节。如果你把max_replication_slots从缺省值10变成32,增加了 22 * 280 = 6160个字节,开销是极小的。

 

结论:

建议把max_replication_slots调整为32,避免未来重启数据库。

 

最新回复 (0)
返回
发新帖