一. 前言
openGauss中的shared_buffers的配置项其实指的是共享缓冲区的大小,主要是用于配置缓冲数据块的大小。本文主要是通过走读代码了解在openGuass中shared_buffers的作用原理。
二. shared_buffers初始化
openGuass在初始化的时候,会将share_buffers的大小转换成block个数,因此事实上,share_buffer是通过缓存数据block进行缓存管理的。缓存的block的个数存放在g_instance.attr.attr_storage.NBuffers中,主要的初始化代码如下所示:
set_config_option
switch (record->vartype) {
case PGC_INT64:
validate_conf_int
validate_conf_int64
parse_int64
if (flags & GUC_UNIT_MEMORY)
MemoryUnitConvert(&endptr, val, flags, hintmsg);
if (strncmp(*endptr, "kB", 2) == 0)
val /= (double)(BLCKSZ / 1024); // 将配置的内存大小转换成BLOCKS的个数
*conf->variable = newval // variable指向g_instance.attr.attr_storage.NBuffers,将BLOCKS的个数存储在g_instance.attr.attr_storage.NBuffers中
}
在InitBufferPool中,会根据variable指向g_instance.attr.attr_storage.NBuffers的大小尽心内存分配,如下所示:
t_thrd.storage_cxt.BufferDescriptors = ShmemInitStruct(TOTAL_BUFFER_NUM * sizeof(BufferDescPadded))
t_thrd.storage_cxt.BufferBlocks = ShmemInitStruct("Buffer Blocks")
后续,block相关的缓存就存放在t_thrd.storage_cxt.BufferDescriptors和t_thrd.storage_cxt.BufferBlocks中。
三. shared_buffers没命中缓存但是缓存还没满时
在数据数据block的时候,刚开始总是会没命中缓存但是block还是处于空闲的状态,openGuass中的处理如下:
缓存相关的读取函数入口在StrategyGetBuffer中,因此开始时候,缓存总是没匹配但是空间是充足的,在get_buf_from_candidate_list的时候,直接找到一个空的buf并且返回,如下所示:
static BufferDesc* get_buf_from_candidate_list(BufferAccessStrategy strategy, uint64* buf_state)
{
....
while (candidate_buf_pop(&g_instance.ckpt_cxt_ctl->pgwr_procs.writer_proc[thread_id].normal_list, &buf_id)) {
if (g_instance.ckpt_cxt_ctl->candidate_free_map[buf_id]) { //candidate_free_map为真说明对应的buf为空
g_instance.ckpt_cxt_ctl->candidate_free_map[buf_id] = false; // 设置为非空闲
AddBufferToRing(strategy, buf); // 保存到缓存中
return buf;
}
}
....
}
四. shared_buffers没命中缓存缓存已经满时
如果缓存已满,那么需要使用淘汰算法进行缓存置换,pg采用时钟扫描页面淘汰算法进行淘汰页面的选择,但是openGuass只是采用简单的非脏即被淘汰的算法,如下为代码流程:
BufferDesc* StrategyGetBuffer(BufferAccessStrategy strategy, uint64* buf_state)
{
....
for (;;)
{
buf = GetBufferDescriptor(ClockSweepTick(max_buffer_can_use));
....
retry_lock_status.retry_times = 0;
// 因为BUF_STATE_GET_REFCOUNT(local_buf_state) == 0 会一直成立,因此实际条件成了只要页面非脏,即被选中淘汰
if (BUF_STATE_GET_REFCOUNT(local_buf_state) == 0 && !(local_buf_state & BM_IS_META) && (backend_can_flush_dirty_page() || !(local_buf_state & BM_DIRTY))) {
*buf_state = local_buf_state;
return buf;
}
}
....
}
五. shared_buffers命中缓存时
命中缓存时直接返回缓存的buf即可,代码如下所示:
BufferDesc* StrategyGetBuffer(BufferAccessStrategy strategy, uint64* buf_state)
{
....
if (strategy != NULL) {
buf = GetBufferFromRing(strategy, buf_state);
if (buf != NULL) {
return buf;
}
}
.....
}