1.基本原理
缓冲管理器:主要是管理共享内和持久存储之间的数据传输,并可能对 DBMS 的性能产生重大影响。
缓冲区管理器、持久存储和后端进程之间的关系如下图所示:
2.缓冲区管理器结构
缓冲区管理器包括一个缓冲区表、缓冲区描述符和缓冲池。
缓冲区描述符:保存着页面的元数据,对应的页面则保存在缓冲池的槽位中。
缓冲池:缓冲池存储数据文件的页,如表的页面。缓冲池是一个数组,每个插槽存储数据文件的一页。缓冲池数组的索引称为buffer_id。
相关函数:BufferDesc *BufferAlloc
2.1 缓冲区标签
数据库为所有数据文件的每个页面分配一个唯一的标记,即缓冲区标签。
缓冲区标签由关系文件节点、关系分支编号和页面块号
typedef struct buftag {
RelFileNode rnode; * physical relation identifier */
ForkNumber forkNum;
BlockNumber blockNum; * blknum relative to begin of reln */
} BufferTag;
(1)关系文件节点用于定位页面所属的关系,包含了表空间、数据库和表的oid。例如:(表空间,数据库, 表) -> (1663, 16384, 37721)
(2)关系分支编号用于定位关系文件的具体分支文件,一个关系可能有三种分支,分别是关系主体(main分支,编号为0)、空闲空间映射( fsm分支,编号为1)及可见性映射(vm分支,编号为2)
(3)页面块号则在具体分支文件中指明相应页面的偏移量,即页面号
例如,{(1663, 16384, 37721), 0, 7} 标签表示,在某个表空间(oid=1663)中,某个数据库(oid=16384)的某张表(oid=37721)的 0 号分支( 0代表关系表本体)的第 7 号页面。再比如,缓冲区标签 {(1663, 16384, 37721), 1, 3} 表示该表空闲空间映射文件的三号页面。关系本体 main 分支编号为0,空闲空间映射 fsm 分支编号为1。
2.2 缓冲区表
缓冲区表:一个散列表(hash 表),存储着页面的buffer_tag与缓冲区描述符的 buffer_id 之间的映射关系。
缓冲表在逻辑上可分为三部分: 散列函数、散列桶槽、数据项
散列函数:hashquickany
散列桶槽:key值
数据项: (页面的 buffer_tag,包含页面元数据的描述符的 buffer_id)
Notes
为了避免哈希函数的冲突,缓冲表采用了使用链表的分离链接方法来解决冲突。当数据项被映射至同一个桶槽时,该方法会将数据项(包括两个值,即页面的 buffer_tag 和包含页面元数据的描述符的 buffer_id)保存在一个链表中。例如,数据项 (Tag_A,id=1)表示在 buffer_id=1 对应的缓冲区描述符中,存储着页面 Tag_A 的元数据
页面简单访问流程:根据后端进程发送的请求,创建目标页面的 buffer_tag,然后将 buffer_tag 通过内置的散列函数映射到哈希桶槽,并分配 buffer_id, 即目标页面在缓冲池数组中存储的槽位的序号。
2.3缓冲区描述符
缓冲区描述符:保存着页面的元数据,对应的页面则保存在缓冲池的槽位中。缓冲区描述符的结构由BufferDesc结构定义。
2.3.1 缓冲区描述符结构体
结构体BufferDesc定义如下:
typedef struct BufferDesc {
BufferTag tag; * ID of page contained in buffer */
/* state of the tag, containing flags, refcount and usagecount */
pg_atomic_uint64 state;
int buf_id; * buffer's index number (from 0) */
ThreadId wait_backend_pid; * backend PID of pin-count waiter */
LWLock* io_in_progress_lock; * to wait for I/O to complete */
LWLock* content_lock; * to lock access to buffer contents */
BufferDescExtra *extra;
#ifdef USE_ASSERT_CHECKING
volatile uint64 lsn_dirty;
#endif
} BufferDesc;
参数说明:
tag:保存着目标页面的buffer_tag(2.1 缓冲区标签),缓冲区标签中对应的页面存储在相应的缓冲池槽中。
buffer_id:标识了缓冲区描述符,相当于对应缓冲池槽的buffer_id。
context_lock:轻量级锁,用于控制对相关页面的访问。
io_in_progress_lock: 轻量级锁,用于控制对相关页面的访问。
state: 用于保存相应页面的状态,主要状态如下:
/*
* Flags for buffer descriptors
*
* Note: TAG_VALID essentially means that there is a buffer hashtable
* entry associated with the buffer's tag.
*/
#define BM_IN_MIGRATE (1U tag.rnode.relNode,
buf->tag.blockNum, buf->tag.forkNum)));
SimpleMarkBufDirty(buf);
*oldFlags |= BM_DIRTY;
*needGetLock = true;
}
5.相关系统对象
5.1 参数
shared_buffers
5.2 函数或视图
(1)系统函数pg_buffercache_pages
select count(*) from pg_buffercache_pages();
观测参数:bufferid, relfilenode, bucketid, storage_type, reltablespace, reldatabase, relforknumber, relblocknumber, isdirty, isvalid, usage_count, pinning_backends
(2)系统函数local_candidate_stat
select count(*) from local_candidate_stat;
观测参数:node_name, candidate_slots, get_buf_from_list, get_buf_clock_sweep, seg_candidate_slots, seg_get_buf_from_list, seg_get_buf_clock_sweep
本文借鉴博客
https://blog.csdn.net/hmxz2nn/article/details/118526453
自旋锁技术博客:https://zhuanlan.zhihu.com/p/659527782