PostgreSQL内存上下文

news/2024/7/9 20:36:49 标签: postgresql, 数据库

不管是什么样的数据库系统,存储管理的本质都是一样的:如何减少I/O次数。内存的访问速度至少是磁盘的数十万倍,所以通常读写磁盘所用的时间决定了数据库操作的总时间,而内存的访问时间可以忽略不计。因此,要尽可能的提高I/O命中率,让最可能被使用的文件块停留在内存中。除此之外,内存管理还是整个数据库系统的桥梁,每一个模块都会使用到内存进行函数运行、缓冲、消息传递等,内存管理对于数据库来说十分重要。

PostgreSQL7.1之前,大量以指针传值的查询可能会造成严重、不易排查的内存泄漏。从7.1版本开始,PostgreSQL使用内存上下文机制来管理内存。一个内存上下文就相当于一个进程的内存环境,每个进程的内存上下文组成一个树行结构,其根节点为TopMemoryContext,下面可以有很多字节点,如用于管理Cache的CacheMemoryContext、用于错误处理的ErrorContext、用于消息传递的MessageMemoryContext等,每个子节点又可以有自己的子节点。通过树形结构可以跟踪进程中内存上下文的创建和使用情况,每个节点定义如下:

typedef struct MemoryContextData
{
	NodeTag		type;			/* 节点类型 */
    /* 以下两个字段放在这里可以减小字节对齐的浪费: */
    bool		isReset;		/* 为真表示从上次reset后没有分配过内存空间 */
	bool		allowInCritSection; /* 允许在critical section(临界区)使用palloc */
	Size		mem_allocated;	/* 该内存上下文中已经分配的内存大小 */
	const MemoryContextMethods *methods;	/* 内存处理函数的指针 */
	MemoryContext parent;		/* 父节点 */
	MemoryContext firstchild;	/* 第一个孩子节点 */
	MemoryContext prevchild;	/* 前一个兄弟节点 */
	MemoryContext nextchild;	/* 后一个兄弟节点 */
	const char *name;			/* 内存上下文的名称(用于调试) */
	const char *ident;			/* 内存上下文的id (用于调试) */
	MemoryContextCallback *reset_cbs;	/* reset/delete 回调函数的链表 */
} MemoryContextData;
typedef struct MemoryContextData *MemoryContext;

在任何时候都有一个当前的内存上下文,记录在全局变量CurrentMemoryContext里,进程在这个内存上下文中调用palloc函数来分配内存。在变化内存上下文时,可以使用MemoryContextSwitchTo切换。

MemoryContext中的methods字段是一系列包含了对内存上下文进行操作的函数,使用函数指针模拟虚函数设计,可以有不同的实现,定义如下:

typedef struct MemoryContextMethods
{
	void	   *(*alloc) (MemoryContext context, Size size);        // 分配内存
	void		(*free_p) (MemoryContext context, void *pointer);   // 释放内存
	void	   *(*realloc) (MemoryContext context, void *pointer, Size size);   // 重分配内存
	void		(*reset) (MemoryContext context);                       // 重置内存上下文
	void		(*delete_context) (MemoryContext context);                 // 删除内存上下文
	Size		(*get_chunk_space) (MemoryContext context, void *pointer);  // 检查内存片段的大小
	bool		(*is_empty) (MemoryContext context);                        // 检查内存上下文是否为空
	void		(*stats) (MemoryContext context,                            // 内存上下文状态
						  MemoryStatsPrintFunc printfunc, void *passthru,
						  MemoryContextCounters *totals,
						  bool print_to_stderr);
#ifdef MEMORY_CONTEXT_CHECKING
	void		(*check) (MemoryContext context);                   // 检查所有内存片段
#endif
} MemoryContextMethods;

MemoryContext并不管理实际的内存块,仅仅作为一个头部记录在AllocSetContext结构里,来管理内存上下文之间的树形关系,真正拥有并管理内存块的是AllocSet,定义如下:

typedef struct AllocSetContext
{
	MemoryContextData header;	/* 内存上下文头部 */
	/* 此上下文中分配的内存信息: */
	AllocBlock	blocks;			/* 内存块链表 */
	AllocChunk	freelist[ALLOCSET_NUM_FREELISTS];	/* 空闲内存片数组 */
	/* 此上下文分配内存相关参数: */
	Size		initBlockSize;	/* 初始内存块大小 */
	Size		maxBlockSize;	/* 最大内存块大小 */
	Size		nextBlockSize;	/* 下一个要分配的内存块大小 */
	Size		allocChunkLimit;	/* 分配内存片的大小阀值 */
	AllocBlock	keeper;			/* 保存在keeper中的内存块在内存上下文重值时会被保留不释放 */
	int			freeListIndex;	/* 此上下文在空闲上下文数组中的位置,不在即为-1 */
} AllocSetContext;
typedef AllocSetContext *AllocSet;

AllocSet所管理的内存区域被分成若干个内存块(AllocBlock),通过标准库函数malloc进行分配,形成一个链表,blocks字段指向这个内存块链表的头部:

typedef struct AllocBlockData
{
	AllocSet	aset;			/* 该内存块所在的AllocSet */
	AllocBlock	prev;			/* 上一个内存块的地址 */
	AllocBlock	next;			/* 下一个内存块的地址 */
	char	   *freeptr;		/* 该内存块空闲区域的首地址 */
	char	   *endptr;			/* 该内存块的末地址 */
}			AllocBlockData;
typedef struct AllocBlockData *AllocBlock;

在每个内存块中可以进一步分配内存,产生的内存片段叫做内存片(AllocChunk),包括一个头部和数据区域,AllocChunk为内存片的头部,数据区域则紧跟在头部信息之后分配,通过pallocpfree函数可以在内存上下文中申请、释放内存片,被释放的内存片将被加入到freelist中以备重复使用,AllocChunk定义如下:

typedef struct AllocChunkData
{
	/* 内存片的实际大小,由于内存片都是以2的幂为大小进行对齐,因此申请的大小可能比实际大小要小*/
	Size		size;

#ifdef MEMORY_CONTEXT_CHECKING
	/* 调试内存使用情况时,存储实际的被使用的空间大小,如果是空闲内存片则为0 */
	Size		requested_size;

#define ALLOCCHUNK_RAWSIZE  (SIZEOF_SIZE_T * 2 + SIZEOF_VOID_P)
#else
#define ALLOCCHUNK_RAWSIZE  (SIZEOF_SIZE_T + SIZEOF_VOID_P)
#endif							/* MEMORY_CONTEXT_CHECKING */

	/* 如果需要,确保内存对齐 */
#if (ALLOCCHUNK_RAWSIZE % MAXIMUM_ALIGNOF) != 0
	char		padding[MAXIMUM_ALIGNOF - ALLOCCHUNK_RAWSIZE % MAXIMUM_ALIGNOF];
#endif

	void	   *aset;	// 该内存片所在的AllocSet,如果内存片为空闲,则用于链接其空闲链表
	// 该结构体后不能有内存对齐,本身应该是对齐的!
}		
typedef struct AllocChunkData *AllocChunk;

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

相关文章

git重置或还原已提交的代码--git reset和git revert

文章目录背景两种方式git resetgit revert背景 在多人协作时,对代码的管理,难免会出现错误。如果出现错误,我们分析下来是要进行一些回退操作,那么解决办法有如下两种: git reset git revert 两种方式 git reset 在…

PostgreSQL文本搜索(一)——简介

PostgreSQL文本搜索(一)——简介 文本搜索对于数据库系统来说是一个十分重要的功能,它可以在数据库文本文档(包括表中的text字段等)中搜索一个模式,可以是一个或多个单词、短语、短句,并且可以…

git clone时出现error: RPC failed; curl 18 transfer closed with outstanding read data remaining

目录问题原因1解决原因2解决打开下面的网站获取信息配置更新DNS问题 使用git bash 从github clone代码时遇到下面的错误 error: RPC failed; curl 18 transfer closed with outstanding read data remaining fatal: The remote end hung up unexpectedly fatal: early EOF fa…

PostgreSQL文本搜索(二)——表和索引

PostgreSQL文本搜索(二)——表和索引 上一节的例子说明了使用简单的常量字符串进行全文匹配。本节展示了如何搜索表数据,或选择使用索引。 搜索表 可以在没有索引的情况下进行全文检索。一个简单的查询是打印在body字段中包含friend这个词…

索引 - 学习总结

目录概述什么是索引索引的优缺点MySQL中索引的基本用法创建索引删除索引测试索引SQL SEERVER中索引的基本用法创建索引创建聚集索引创建复合索引创建覆盖索引创建唯一索引查看索引MySQL设计索引的原则SQL Server索引设计原则索引的数据结构聚集索引和非聚集索引复合索引的使用参…

PostgreSQL文本搜索(五)——词法分析器

PostgreSQL文本搜索(五)——词法分析器 文本搜索词法分析器负责将原始文档文本分割成token,并确定每个token的类型,其中可能的类型集合由词法分析器本身定义。注意分析器根本不修改文本,它只是识别合理的词的边界。由…

解决:PLS-00103: Encountered the symbol “CREATE”

文章目录问题描述问题解决问题描述 在oracle中,需要修改一个PACKAGE中某一个procedure的逻辑,修改完后,将 package定义和package body放在一个脚本中来执行时,遇到下面的错误: PLS-00103: Encountered the symbol “C…

PostgreSQL文本搜索(六)——词典

PostgreSQL文本搜索(六)——词典 词典用于消除在搜索中不应考虑的词(停顿词),并使词规范化,以便同一词的不同派生形式能够匹配。一个成功规范化的词被称为lexeme(词位、词素)。除了…