zl程序教程

您现在的位置是:首页 >  数据库

当前栏目

深入浅出PostgreSQL性能调优

postgresql性能 深入浅出 调优
2023-06-13 09:17:21 时间

PostgreSQL性能调优概览

PostgreSQL 参数类型

  • struct config_generic定义了所有PG参数的通用结构;struct config_{int/real/string/bool}struct config_generic之上进行分组定义了不同数据类型的参数类型

深入PostgreSQL 性能参数

shared_buffers参数

  • PostgreSQL中按照参数的类型(int/bool/string/real)等类型进行分组进行解析。但是针对shared_buffers在配置文件中可以内存大小(单位可是是KB/MB/GB/TB)。在shared_buffers定义在struct config_int中。NBuffers全局变量根据shared_buffers大小计算出page的个数,根据这个来初始化shared buffer pool
static struct config_int ConfigureNamesInt[] ={
	// shared_buffers定义
	{
		// 设置shared_buffers的参数名称、参数级别、参数资源类型
		{"shared_buffers", PGC_POSTMASTER, RESOURCES_MEM,
			gettext_noop("Sets the number of shared memory buffers used by the server."),
			NULL,
	
			GUC_UNIT_BLOCKS		},
		// 根据shared_buffers设置的内存大小,结算出缓存多少个page的个数,然后在申请资源进行初始化整个buffer pool
		// 参数对应的PG内部的单位是数据页,比如设置100M,则最终的shared_buffers在PG内部对应参数绑定的是(100*1024/(数据页/1024))
		&NBuffers,
		1024, 16, INT_MAX / 2,
		NULL, NULL, NULL
	},}
  • shared_buffer的初始化链路如下,从这里可以看出shared_buffer不能再PG运行期间动态进行调整,调整整个参数后需要重启PostgreSQL服务。这里有一个优化调整shared_buffer不重启的思路,是否可以把shared buffer pool改变为多个子池,整个shared pool包含最大个子池指针数组,动态调整值需要再对应的数组指针下标申请空间,可以这么做但是需要考虑资源互斥、脏页刷新等。
void InitBufferPool(void){
	bool		foundBufs,
				foundDescs,
				foundIOCV,
				foundBufCkpt;

	// buffer pool 中page的header申请
	BufferDescriptors = (BufferDescPadded *)
		ShmemInitStruct("Buffer Descriptors",
						NBuffers * sizeof(BufferDescPadded),
						&foundDescs);

	// buffer pool中数据块空间申请
	BufferBlocks = (char *)
		ShmemInitStruct("Buffer Blocks",
						NBuffers * (Size) BLCKSZ, &foundBufs);

	/* Align condition variables to cacheline boundary. */
	BufferIOCVArray = (ConditionVariableMinimallyPadded *)
		ShmemInitStruct("Buffer IO Condition Variables",
						NBuffers * sizeof(ConditionVariableMinimallyPadded),
						&foundIOCV);


	// 用来排序已经做了checkpoint的page head的数组 
	CkptBufferIds = (CkptSortItem *)
		ShmemInitStruct("Checkpoint BufferIds",
						NBuffers * sizeof(CkptSortItem), &foundBufCkpt);

	if (foundDescs || foundBufs || foundIOCV || foundBufCkpt)
	{

		Assert(foundDescs && foundBufs && foundIOCV && foundBufCkpt);
	}
	else
	{
		int			i;
		// 初始化buffer pool中的每个page header
		for (i = 0; i < NBuffers; i++)
		{
			BufferDesc *buf = GetBufferDescriptor(i);

			CLEAR_BUFFERTAG(buf->tag);

			pg_atomic_init_u32(&buf->state, 0);
			buf->wait_backend_pid = 0;
			// 设定page id
			buf->buf_id = i;

		
			// 链接下一个page header
			buf->freeNext = i + 1;

			LWLockInitialize(BufferDescriptorGetContentLock(buf),
							 LWTRANCHE_BUFFER_CONTENT);

			ConditionVariableInit(BufferDescriptorGetIOCV(buf));
		}

		/* Correct last entry of linked list */
		GetBufferDescriptor(NBuffers - 1)->freeNext = FREENEXT_END_OF_LIST;
	}
	// 初始化shared pool剩下的事务
	StrategyInitialize(!foundDescs);

	// 初始化后端文件flush的上下文
	WritebackContextInit(&BackendWritebackContext,
						 &backend_flush_after);}

wal_buffers参数

  • PostgreSQL中按照参数的类型(int/bool/string/real)等类型进行分组进行解析。但是针对wal_buffers在配置文件中可以内存大小(单位可是是KB/MB/GB/TB)。定义如下
static struct config_int ConfigureNamesInt[] ={
	// wal_buffers定义
	{
		// 参数绑定
		{"wal_buffers", PGC_POSTMASTER, WAL_SETTINGS,
			gettext_noop("Sets the number of disk-page buffers in shared memory for WAL."),
			NULL,
			// 设置单位
			GUC_UNIT_XBLOCKS		},
		// 绑定wal_buffers对应的变量
		&XLOGbuffers,
		-1, -1, (INT_MAX / XLOG_BLCKSZ),
		check_wal_buffers, NULL, NULL
	},}
  • wal log buffer初始化过程如下,同样的这个参数不能再PostgreSQL运行期动态更改,如果更改必须重启PostgreSQL服务
void XLOGShmemInit(void){
	bool		foundCFile,
				foundXLog;
	char	   *allocptr;
	int			i;
	ControlFileData *localControlFile;// 如果编译PG时候有带有WAL_DEBUG的编译,可以在PG中进行wal 日志输出的Debug#ifdef WAL_DEBUG
	// 创建wal debug的上下文和内存初始化
	if (walDebugCxt == NULL)
	{
		walDebugCxt = AllocSetContextCreate(TopMemoryContext,
											"WAL Debug",
											ALLOCSET_DEFAULT_SIZES);
		MemoryContextAllowInCriticalSection(walDebugCxt, true);
	}#endif

	// 获取wal 日志文件的控制文件
	XLogCtl = (XLogCtlData *)
		ShmemInitStruct("XLOG Ctl", XLOGShmemSize(), &foundXLog);

	localControlFile = ControlFile;
	ControlFile = (ControlFileData *)
		ShmemInitStruct("Control File", sizeof(ControlFileData), &foundCFile);

	if (foundCFile || foundXLog)
	{
		/* both should be present or neither */
		Assert(foundCFile && foundXLog);

		/* Initialize local copy of WALInsertLocks */
		WALInsertLocks = XLogCtl->Insert.WALInsertLocks;

		if (localControlFile)
			pfree(localControlFile);
		return;
	}
	memset(XLogCtl, 0, sizeof(XLogCtlData));

	// 如果存在本地的控制文件读取然后初始化到内存的控制文件结构中
	if (localControlFile)
	{
		memcpy(ControlFile, localControlFile, sizeof(ControlFileData));
		pfree(localControlFile);
	}

	// 重置内存控制文件中的xlblocks
	allocptr = ((char *) XLogCtl) + sizeof(XLogCtlData);
	XLogCtl->xlblocks = (XLogRecPtr *) allocptr;
	// 重置了xlog控制文件中的wal指针缓存区
	memset(XLogCtl->xlblocks, 0, sizeof(XLogRecPtr) * XLOGbuffers);
	allocptr += sizeof(XLogRecPtr) * XLOGbuffers;


	/* WAL insertion locks. Ensure they're aligned to the full padded size */

	allocptr += sizeof(WALInsertLockPadded) -
		((uintptr_t) allocptr) % sizeof(WALInsertLockPadded);
	WALInsertLocks = XLogCtl->Insert.WALInsertLocks =
		(WALInsertLockPadded *) allocptr;
	allocptr += sizeof(WALInsertLockPadded) * NUM_XLOGINSERT_LOCKS;

	for (i = 0; i < NUM_XLOGINSERT_LOCKS; i++)
	{
		LWLockInitialize(&WALInsertLocks[i].l.lock, LWTRANCHE_WAL_INSERT);
		WALInsertLocks[i].l.insertingAt = InvalidXLogRecPtr;
		WALInsertLocks[i].l.lastImportantAt = InvalidXLogRecPtr;
	}

	// 重置日志page缓冲区
	allocptr = (char *) TYPEALIGN(XLOG_BLCKSZ, allocptr);
	XLogCtl->pages = allocptr;
	memset(XLogCtl->pages, 0, (Size) XLOG_BLCKSZ * XLOGbuffers);


	// 完成后续日志控制结构的剩余结构初始化
	XLogCtl->XLogCacheBlck = XLOGbuffers - 1;
	XLogCtl->SharedRecoveryState = RECOVERY_STATE_CRASH;
	XLogCtl->SharedHotStandbyActive = false;
	XLogCtl->SharedPromoteIsTriggered = false;
	XLogCtl->WalWriterSleeping = false;

	SpinLockInit(&XLogCtl->Insert.insertpos_lck);
	SpinLockInit(&XLogCtl->info_lck);
	SpinLockInit(&XLogCtl->ulsn_lck);
	InitSharedLatch(&XLogCtl->recoveryWakeupLatch);
	ConditionVariableInit(&XLogCtl->recoveryNotPausedCV);}