4.3.3 从buf淘汰方式看改进---freelist
在“4.1.4.5 Buf置换管理算法”中,我们描述了buf调度算法。PG首先是从freelist中取出空余的优先供缓存请求使用。如果freelist空,才去真正淘汰缓存块。
所以,一个思路是:尽可能增加freelist中的成员,使得淘汰减少,是否物理IO也可以减少呢?
先看PG对于freelist的操作方式:
1. 从结构上看,首先定义出一个指针,用来指向空余的buf,见如下蓝色字体
typedef struct BufferAccessStrategyData
{
BufferAccessStrategyType btype;
int ring_size;
int current;
bool current_was_in_ring;
/*
* Array of buffer numbers. InvalidBuffer (that is, zero) indicates we
* have not yet selected a buffer for this ring slot. For allocation
* simplicity this is palloc'd together with the fixed fields of the
* struct.
*/
Buffer buffers[1]; /* VARIABLE SIZE ARRAY */
} BufferAccessStrategyData;
2. 从这个指针的调用关系看,共被:增加空余buf到链表操作(AddBufferToRing)和从链表取出空余buf 操作构成(GetBufferFromRing)
BufferAccessStrategyData.buffers
BufferAccessStrategyData(就是如上的数据结构)
GetBufferFromRing
AddBufferToRing
StrategyRejectBuffer
3. 从函数的调用关系看,GetBufferFromRing和AddBufferToRing函数,都是被StrategyGetBuffer函数调用。而当有缓存块被申请使用时,才会调用到StrategyGetBuffer函数(从StrategyGetBuffer函数的代码中可以看出,只有使用指定“淘汰策略”,才会从一个“Ring”上拿出空余的buf供上层使用)。这样,freelist上的空余buf增加的机会并不多。这样,可能会影响着buf的有效利用。

/*
* If given a strategy object, see whether it can select a buffer. We
* assume strategy objects don't need the BufFreelistLock.
*/
if (strategy != NULL)
{
buf = GetBufferFromRing(strategy);
if (buf != NULL)
{
*lock_held = false;
return buf;
}
}
所以,我们可以知道,此处也有改进余地。
改法:增加一个线程,在适当的时机,遍历缓冲区,当发现空余的缓冲区存在时,即补充到freelist上,这样当需要申请缓冲区时,freelist尽可能地多存在空闲缓存块。
什么时候让这个线程启动去干活呢?上一节,增加了异步IO操作(提出了预取功能)。当发出异步IO读写请求时,数据还没有到位,也许是启动这样线程的最佳时机。