这篇文章主要讲解了“PostgreSQL checkpoint中用于刷一个脏page的函数是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“PostgreSQL checkpoint中用于刷一个脏page的函数是什么”吧!
一、数据结构
宏定义
checkpoints request flag bits,检查点请求标记位定义.
#define CHECKPOINT_IS_SHUTDOWN 0x0001
#define CHECKPOINT_END_OF_RECOVERY 0x0002
#define CHECKPOINT_IMMEDIATE 0x0004
#define CHECKPOINT_FORCE 0x0008
#define CHECKPOINT_FLUSH_ALL 0x0010
#define CHECKPOINT_WAIT 0x0020
#define CHECKPOINT_REQUESTED 0x0040
#define CHECKPOINT_CAUSE_XLOG 0x0080
#define CHECKPOINT_CAUSE_TIME 0x0100
二、源码解读
SyncOneBuffer,在syncing期间处理一个buffer,其主要处理逻辑如下:
1.获取buffer描述符
2.锁定buffer
3.根据buffer状态和输入参数执行相关判断/处理
4.钉住脏页,上共享锁,调用FlushBuffer刷盘
5.解锁/解钉和其他收尾工作
static int
SyncOneBuffer(int buf_id, bool skip_recently_used, WritebackContext *wb_context)
{
BufferDesc *bufHdr = GetBufferDescriptor(buf_id);
int result = 0;
uint32 buf_state;
BufferTag tag;
ReservePrivateRefCountEntry();
buf_state = LockBufHdr(bufHdr);
if (BUF_STATE_GET_REFCOUNT(buf_state) == 0 &&
BUF_STATE_GET_USAGECOUNT(buf_state) == 0)
{
result |= BUF_REUSABLE;
}
else if (skip_recently_used)
{
//跳过最近使用的buffer
UnlockBufHdr(bufHdr, buf_state);
return result;
}
if (!(buf_state & BM_VALID) || !(buf_state & BM_DIRTY))
{
//buffer无效或者不是脏块
UnlockBufHdr(bufHdr, buf_state);
return result;
}
PinBuffer_Locked(bufHdr);
LWLockAcquire(BufferDescriptorGetContentLock(bufHdr), LW_SHARED);
//调用FlushBuffer
//If the caller has an smgr reference for the buffer's relation, pass it as the second parameter.
//If not, pass NULL.
FlushBuffer(bufHdr, NULL);
LWLockRelease(BufferDescriptorGetContentLock(bufHdr));
tag = bufHdr->tag;
UnpinBuffer(bufHdr, true);
ScheduleBufferTagForWriteback(wb_context, &tag);
return result | BUF_WRITTEN;
}
FlushBuffer
FlushBuffer函数物理上把共享缓存刷盘,主要实现函数还是smgrwrite(storage manager write).
static void
FlushBuffer(BufferDesc *buf, SMgrRelation reln)
{
XLogRecPtr recptr;
ErrorContextCallback errcallback;
instr_time io_start,
io_time;
Block bufBlock;
char *bufToWrite;
uint32 buf_state;
if (!StartBufferIO(buf, false))
return;
errcallback.callback = shared_buffer_write_error_callback;
errcallback.arg = (void *) buf;
errcallback.previous = error_context_stack;
error_context_stack = &errcallback;
if (reln == NULL)
reln = smgropen(buf->tag.rnode, InvalidBackendId);
TRACE_POSTGRESQL_BUFFER_FLUSH_START(buf->tag.forkNum,
buf->tag.blockNum,
reln->smgr_rnode.node.spcNode,
reln->smgr_rnode.node.dbNode,
reln->smgr_rnode.node.relNode);
buf_state = LockBufHdr(buf);
recptr = BufferGetLSN(buf);
buf_state &= ~BM_JUST_DIRTIED;
UnlockBufHdr(buf, buf_state);
if (buf_state & BM_PERMANENT)
XLogFlush(recptr);
bufBlock = BufHdrGetBlock(buf);
bufToWrite = PageSetChecksumCopy((Page) bufBlock, buf->tag.blockNum);
if (track_io_timing)
INSTR_TIME_SET_CURRENT(io_start);
smgrwrite(reln,
buf->tag.forkNum,
buf->tag.blockNum,
bufToWrite,
false);
if (track_io_timing)
{
INSTR_TIME_SET_CURRENT(io_time);
INSTR_TIME_SUBTRACT(io_time, io_start);
pgstat_count_buffer_write_time(INSTR_TIME_GET_MICROSEC(io_time));
INSTR_TIME_ADD(pgBufferUsage.blk_write_time, io_time);
}
pgBufferUsage.shared_blks_written++;
TerminateBufferIO(buf, true, 0);
TRACE_POSTGRESQL_BUFFER_FLUSH_DONE(buf->tag.forkNum,
buf->tag.blockNum,
reln->smgr_rnode.node.spcNode,
reln->smgr_rnode.node.dbNode,
reln->smgr_rnode.node.relNode);
error_context_stack = errcallback.previous;
}
三、跟踪分析
测试脚本
testdb=# update t_wal_ckpt set c2 = 'C4#'||substr(c2,4,40);
UPDATE 1
testdb=# checkpoint;
跟踪分析
(gdb) handle SIGINT print nostop pass
SIGINT is used by the debugger.
Are you sure you want to change it? (y or n) y
Signal Stop Print Pass to program Description
SIGINT No Yes Yes Interrupt
(gdb) b SyncOneBuffer
Breakpoint 1 at 0x8a7167: file bufmgr.c, line 2357.
(gdb) c
Continuing.
Program received signal SIGINT, Interrupt.
Breakpoint 1, SyncOneBuffer (buf_id=0, skip_recently_used=false, wb_context=0x7fff27f5ae00) at bufmgr.c:2357
2357 BufferDesc *bufHdr = GetBufferDescriptor(buf_id);
(gdb) n
2358 int result = 0;
(gdb) p *bufHdr
$1 = {tag = {rnode = {spcNode = 1663, dbNode = 16384, relNode = 221290}, forkNum = MAIN_FORKNUM, blockNum = 0}, buf_id = 0,
state = {value = 3548905472}, wait_backend_pid = 0, freeNext = -2, content_lock = {tranche = 53, state = {
value = 536870912}, waiters = {head = 2147483647, tail = 2147483647}}}
(gdb) n
2362 ReservePrivateRefCountEntry();
(gdb)
2373 buf_state = LockBufHdr(bufHdr);
(gdb)
2375 if (BUF_STATE_GET_REFCOUNT(buf_state) == 0 &&
(gdb)
2376 BUF_STATE_GET_USAGECOUNT(buf_state) == 0)
(gdb)
2375 if (BUF_STATE_GET_REFCOUNT(buf_state) == 0 &&
(gdb)
2380 else if (skip_recently_used)
(gdb)
2387 if (!(buf_state & BM_VALID) || !(buf_state & BM_DIRTY))
(gdb)
2398 PinBuffer_Locked(bufHdr);
(gdb) p buf_state
$2 = 3553099776
(gdb) n
2399 LWLockAcquire(BufferDescriptorGetContentLock(bufHdr), LW_SHARED);
(gdb)
2401 FlushBuffer(bufHdr, NULL);
(gdb) step
FlushBuffer (buf=0x7fedc4a68300, reln=0x0) at bufmgr.c:2687
2687 if (!StartBufferIO(buf, false))
(gdb) n
2691 errcallback.callback = shared_buffer_write_error_callback;
(gdb)
2692 errcallback.arg = (void *) buf;
(gdb)
2693 errcallback.previous = error_context_stack;
(gdb)
2694 error_context_stack = &errcallback;
(gdb)
2697 if (reln == NULL)
(gdb)
2698 reln = smgropen(buf->tag.rnode, InvalidBackendId);
(gdb)
2700 TRACE_POSTGRESQL_BUFFER_FLUSH_START(buf->tag.forkNum,
(gdb)
2706 buf_state = LockBufHdr(buf);
(gdb)
2712 recptr = BufferGetLSN(buf);
(gdb)
2715 buf_state &= ~BM_JUST_DIRTIED;
(gdb) p recptr
$3 = 16953421760
(gdb) n
2716 UnlockBufHdr(buf, buf_state);
(gdb)
2735 if (buf_state & BM_PERMANENT)
(gdb)
2736 XLogFlush(recptr);
(gdb)
2743 bufBlock = BufHdrGetBlock(buf);
(gdb)
2750 bufToWrite = PageSetChecksumCopy((Page) bufBlock, buf->tag.blockNum);
(gdb) p bufBlock
$4 = (Block) 0x7fedc4e68300
(gdb) n
2752 if (track_io_timing)
(gdb)
2758 smgrwrite(reln,
(gdb)
2764 if (track_io_timing)
(gdb)
2772 pgBufferUsage.shared_blks_written++;
(gdb)
2778 TerminateBufferIO(buf, true, 0);
(gdb)
2780 TRACE_POSTGRESQL_BUFFER_FLUSH_DONE(buf->tag.forkNum,
(gdb)
2787 error_context_stack = errcallback.previous;
(gdb)
2788 }
(gdb)
SyncOneBuffer (buf_id=0, skip_recently_used=false, wb_context=0x7fff27f5ae00) at bufmgr.c:2403
2403 LWLockRelease(BufferDescriptorGetContentLock(bufHdr));
(gdb)
2405 tag = bufHdr->tag;
(gdb)
2407 UnpinBuffer(bufHdr, true);
(gdb)
2409 ScheduleBufferTagForWriteback(wb_context, &tag);
(gdb)
2411 return result | BUF_WRITTEN;
(gdb)
2412 }
(gdb)
感谢各位的阅读,以上就是“PostgreSQL checkpoint中用于刷一个脏page的函数是什么”的内容了,经过本文的学习后,相信大家对PostgreSQL checkpoint中用于刷一个脏page的函数是什么这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!