openGauss内核求索 ---- 缓冲区管理器
Table of Contents
- openGauss内核求索 ---- 缓冲区管理器
- 1.基本原理
- 2.缓冲区管理器结构
- 2.1 缓冲区标签
- 2.2 缓冲区表
- 2.3缓冲区描述符
- 2.3.1 缓冲区描述符结构体
- 2.3.2 缓冲区描述符初始状态
- 2.4 缓冲池
- 2.4.1 缓冲池结构
- 2.4.2 buffer候选队列
- 2.5 缓冲区相关的锁
- 2.5.1 缓冲表锁
- 2.5.2 缓冲区描述符相关的锁
- 3.缓冲区管理器的工作原理
- 3.1 访问存储在缓冲池中的页面
- 3.2 将页面从存储加载到空槽
- 3.3 将页面从存储加载到受害者缓冲池槽
- 4. 相关函数解析
- 4.1 函数BufferAlloc
- 4.1 函数StrategyGetBuffer
- 4.2 函数PageCheckIfCanEliminate
- 5.相关系统对象
- 5.1 参数
- 5.2 函数或视图
1.基本原理
- 1.基本原理
- 2.缓冲区管理器结构
- 2.1 缓冲区标签
- 2.2 缓冲区表
- 2.3缓冲区描述符
- 2.3.1 缓冲区描述符结构体
- 2.3.2 缓冲区描述符初始状态
- 2.4 缓冲池
- 2.4.1 缓冲池结构
- 2.4.2 buffer候选队列
- 2.5 缓冲区相关的锁
- 2.5.1 缓冲表锁
- 2.5.2 缓冲区描述符相关的锁
- 3.缓冲区管理器的工作原理
- 3.1 访问存储在缓冲池中的页面
- 3.2 将页面从存储加载到空槽
- 3.3 将页面从存储加载到受害者缓冲池槽
- 4. 相关函数解析
- 4.1 函数BufferAlloc
- 4.1 函数StrategyGetBuffer
- 4.2 函数PageCheckIfCanEliminate
- 5.相关系统对象
- 5.1 参数
- 5.2 函数或视图
缓冲管理器:主要是管理 共享内存 和 持久存储 之间的数据传输,并可能对 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.dbNode, buf->tag.rnode.relNode,
buf->tag.blockNum, buf->tag.forkNum)));
SimpleMarkBufDirty(buf);
*oldFlags |= BM_DIRTY;
*needGetLock = true;
}
5.相关系统对象
5.1 参数
shared_buffers
5.2 函数或视图
- 系统函数pg_buffercache_pages
select count(*) from pg_buffercache_pages();
观测参数: bufferid, relfilenode, bucketid, storage_type, reltablespace, reldatabase, relforknumber, relblocknumber, isdirty, isvalid, usage_count, pinning_backends
- 系统函数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
author: 丑丑的老太婆(unique woman)