PostgreSQL共享缓冲区部分代码阅读总结一

http://blog.sina.com.cn/s/blog_48c95a190100grf9.html

PostgreSQL中缓冲区管理主要分为共享缓冲区和管理和本地缓冲区管理两大部分,其中共享缓冲区结构以及管理方法主要定义在文件pgsql/src/backend/storage/buffer/buf_init.c,文件pgsql/src/backend/storage/buffer/buf_table.c,文件pgsql/src/backend/storage/buffer/bufmgr.c和文件pgsql/src/backend/storage/buffer/freelist.c中。

我们首先来看文件pgsql/src/backend/storage/buffer/buf_init.c,这个文件主要就是定义了共享缓冲区管理器的初始化方法。在本地缓冲区管理器部分我们知道缓冲区的组织至少包括三个部分:缓冲区块、缓冲区描述器以及引用计数,共享缓冲区也是一样。在文件的开始就定义了这三个全局变量,唯一跟本地缓冲区不同的地方就是,这里定义的变量名前面都没有Local这个字符串,此外这里引用计数的名称前加了一个Private

BufferDesc *BufferDescriptors;

char *BufferBlocks;

int32 *PrivateRefCount;

然后就是一些其他的用于统计信息的计数器变量:

long int ReadBufferCount;

long int ReadLocalBufferCount;

long int BufferHitCount;

long int LocalBufferHitCount;

long int BufferFlushCount;

long int LocalBufferFlushCount;

long int BufFileReadCount;

long int BufFileWriteCount;

其中包括读缓冲区的次数、缓冲区命中的次数、缓冲区文件的读写次数等等,通过这些统计数据我们就可以计算缓冲区命中率一类的更有意义的数据信息。

共享缓冲区因为会涉及到多个事务的并发访问以及日志的记录和检查点的处理,所以相较于本地缓冲区的管理更加复杂,涉及到的数据结构也更加繁琐。在这里,和本地缓冲区一样,我们首先介绍共享缓冲区的初始化。

void

InitBufferPool(void)

{

bool foundBufs,foundDescs;

BufferDescriptors = (BufferDesc *)

ShmemInitStruct("Buffer Descriptors",

NBuffers * sizeof(BufferDesc),&foundDescs);

BufferBlocks = (char *)

ShmemInitStruct("Buffer Blocks",'serif'">NBuffers * (Size) BLCKSZ,&foundBufs);

调用函数ShmemInitStruct为缓冲区描述器和缓冲区块分配空间,和本地缓冲区不同的

是这里没有连同私有引用计数一起分配,函数ShmemInitStruct定义在文件pgsql/src/backend/storage/ipc/shmem.c中,它会根据第一个参数,也就是共享内存数据结构的名称来判断这个结构在共享内存中是否已经存在,如果不存在的话就在共享内存段中分配一个这样的结构,我们稍后会对这个重要的函数进行分析。

if (foundDescs || foundBufs)

{

Assert(foundDescs && foundBufs);

}

如果通过调用函数ShmemInitStruct我们发现缓冲区描述器或者缓冲区块已经分配了的

话我们就要验证这两个结构都是已经分配的,因为这两个结构肯定是一起分配的,如果一个还存在另一个已经不存在的话就说明共享内存段出了问题。

else

BufferDesc *buf;

int i;

buf = BufferDescriptors;

如果这两个结构以前都不存在,都是新创建的话,我们就需要对其进行初始化配置。

在这里,和本地缓冲区一样我们通过循环遍历来对缓冲区描述器进行初始化。

for (i = 0; i < NBuffers; buf++,i++)

{

CLEAR_BUFFERTAG(buf->tag);

buf->flags = 0;

buf->usage_count = 0;

buf->refcount = 0;

buf->wait_backend_pid = 0;

SpinLockInit(&buf->buf_hdr_lock);

buf->buf_id = i;

这里在缓冲区描述器中存放的buf_id就是非负数,即从09991000个缓

冲区块。

buf->freeNext = i + 1;

buf->io_in_progress_lock = LWLockAssign();

buf->content_lock = LWLockAssign();

因为一开始所有的缓冲区块都是空闲的,所以每个缓冲区块的freeNext指针都

指向它的下一个缓冲区块,而最后一个缓冲区块的freeNext域则为FREENEXT_END_OF_LIST

}

BufferDescriptors[NBuffers - 1].freeNext = FREENEXT_END_OF_LIST;

StrategyInitialize(!foundDescs);

这里调用函数StrategyInitialize来对缓冲区策略进行初始化,跟本地缓冲区不一样,共

享缓冲区有一个专门的数据结构类型BufferStrategyControl对缓冲区的替换进行控制,函数StrategyInitialize就是对这个结构类型进行一些初始化的工作。

}

通过上面的分析我们知道InitBufferPool这个函数主要就是实现了对共享缓冲区部分两个重要数据结构的地址分配缓冲区块和缓冲区描述器,其次就是初始化一个专门用于缓冲区替换策略控制的数据结构。在代码的分析中我们已经提到就是InitBufferPool这个函数并没有给缓冲区块一起分配针对缓冲区块的引用计数,所以下面的函数InitBufferPoolAccess就是实现对引用计数的分配的,至于为什么将这两部分分开放在两个函数中还有待进一步探究。

InitBufferPoolAccess(void)

PrivateRefCount = (int32 *) calloc(NBuffers,sizeof(int32));

if (!PrivateRefCount)

ereport(FATAL,'serif'">(errcode(ERRCODE_OUT_OF_MEMORY),'serif'">errmsg("out of memory")));

就是分配一个大小为1000的数组,数组元素类型为整型。

}

文件pgsql/src/backend/storage/buffer/buf_init.c中还有另外一个函数BufferShmemSize,这个函数用来计算缓冲区所需的共享内存的空间大小。

Size

BufferShmemSize(void)

Size size = 0;

size = add_size(size,mul_size(NBuffers,sizeof(BufferDesc)));

这里就是分别计算了缓冲区描述器、缓冲区块以及缓冲区策略所占用的内存地址大小,

然后将三者相加之后返回,这里也没有将引用计数的空间大小计算在内,这不得不使我们隐约地感到引用计数是不算在共享缓冲区里面的,事实真相有待进一步揭示。

return size;

}

相关文章

文章浏览阅读601次。Oracle的数据导入导出是一项基本的技能,...
文章浏览阅读553次。开头还是介绍一下群,如果感兴趣polardb...
文章浏览阅读3.5k次,点赞3次,收藏7次。折腾了两个小时多才...
文章浏览阅读2.7k次。JSON 代表 JavaScript Object Notation...
文章浏览阅读2.9k次,点赞2次,收藏6次。navicat 连接postgr...
文章浏览阅读1.4k次。postgre进阶sql,包含分组排序、JSON解...