文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

PostgreSQL中ReadBuffer_common函数有什么作用

2024-04-02 19:55

关注

这篇文章主要介绍“PostgreSQL中ReadBuffer_common函数有什么作用”,在日常操作中,相信很多人在PostgreSQL中ReadBuffer_common函数有什么作用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”PostgreSQL中ReadBuffer_common函数有什么作用”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

一、数据结构

BufferDesc
共享缓冲区的共享描述符(状态)数据


//buffer header锁定
#define BM_LOCKED               (1U << 22)  
//数据需要写入(标记为DIRTY)
#define BM_DIRTY                (1U << 23)  
//数据是有效的
#define BM_VALID                (1U << 24)  
//已分配buffer tag
#define BM_TAG_VALID            (1U << 25)  
//正在R/W
#define BM_IO_IN_PROGRESS       (1U << 26)  
//上一个I/O出现错误
#define BM_IO_ERROR             (1U << 27)  
//开始写则变DIRTY
#define BM_JUST_DIRTIED         (1U << 28)  
//存在等待sole pin的其他进程
#define BM_PIN_COUNT_WAITER     (1U << 29)  
//checkpoint发生,必须刷到磁盘上
#define BM_CHECKPOINT_NEEDED    (1U << 30)  
//持久化buffer(不是unlogged或者初始化fork)
#define BM_PERMANENT            (1U << 31)  

typedef struct BufferDesc
{
    //buffer tag
    BufferTag   tag;            
    //buffer索引编号(0开始)
    int         buf_id;         
    
    //tag状态,包括flags/refcount和usagecount
    pg_atomic_uint32 state;
    //pin-count等待进程ID
    int         wait_backend_pid;   
    //空闲链表链中下一个空闲的buffer
    int         freeNext;       
    //缓冲区内容锁
    LWLock      content_lock;   
} BufferDesc;

BufferTag
Buffer tag标记了buffer存储的是磁盘中哪个block


typedef struct buftag
{
    //物理relation标识符
    RelFileNode rnode;          
    ForkNumber  forkNum;
    //相对于relation起始的块号
    BlockNumber blockNum;       
} BufferTag;

二、源码解读

ReadBuffer_common函数是所有ReadBuffer相关的通用逻辑,其实现逻辑如下:
1.初始化相关变量和执行相关判断(是否扩展isExtend?是否临时表isLocalBuf?)
2.如为临时表,则调用LocalBufferAlloc获取描述符;否则调用BufferAlloc获取描述符;
 同时,设置是否在缓存命中的标记(变量found)
3.如在缓存中命中
3.1如非扩展buffer,更新统计信息,如有需要,锁定buffer并返回
3.2如为扩展buffer,则获取block
3.2.1如PageIsNew返回F,则报错
3.2.2如为本地buffer(临时表),则调整标记
3.2.3如非本地buffer,则清除BM_VALID标记
4.没有在缓存中命中,则获取block
4.1如为扩展buffer,通过填充0初始化buffer,调用smgrextend扩展
4.2如为普通buffer
4.2.1如模式为RBM_ZERO_AND_LOCK/RBM_ZERO_AND_CLEANUP_LOCK,填充0
4.2.2否则,通过smgr(存储管理器)读取block,如需要,则跟踪I/O时间,同时检查垃圾数据
5.已扩展了buffer或者已读取了block
5.1如需要,锁定buffer
5.2如为临时表,则调整标记;否则设置BM_VALID,中断IO,唤醒等待的进程
5.3更新统计信息
5.4返回buffer


static Buffer
ReadBuffer_common(SMgrRelation smgr, char relpersistence, ForkNumber forkNum,
                  BlockNumber blockNum, ReadBufferMode mode,
                  BufferAccessStrategy strategy, bool *hit)
{
    BufferDesc *bufHdr;//buffer描述符
    Block       bufBlock;//相应的block
    bool        found;//是否命中?
    bool        isExtend;//扩展?
    bool        isLocalBuf = SmgrIsTemp(smgr);//本地buffer?
    *hit = false;
    
    //确保有空间存储buffer pin
    ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
    //如为P_NEW,则需扩展
    isExtend = (blockNum == P_NEW);
    //跟踪
    TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum,
                                       smgr->smgr_rnode.node.spcNode,
                                       smgr->smgr_rnode.node.dbNode,
                                       smgr->smgr_rnode.node.relNode,
                                       smgr->smgr_rnode.backend,
                                       isExtend);
    
    //如调用方要求P_NEW,则替换适当的块号
    if (isExtend)
        blockNum = smgrnblocks(smgr, forkNum);
    if (isLocalBuf)
    {
        //本地buffer(临时表)
        bufHdr = LocalBufferAlloc(smgr, forkNum, blockNum, &found);
        if (found)
            pgBufferUsage.local_blks_hit++;
        else if (isExtend)
            pgBufferUsage.local_blks_written++;
        else if (mode == RBM_NORMAL || mode == RBM_NORMAL_NO_LOG ||
                 mode == RBM_ZERO_ON_ERROR)
            pgBufferUsage.local_blks_read++;
    }
    else
    {
        //非临时表
        
        //获取buffer描述符
        bufHdr = BufferAlloc(smgr, relpersistence, forkNum, blockNum,
                             strategy, &found);
        if (found)
            //在内存中命中
            pgBufferUsage.shared_blks_hit++;
        else if (isExtend)
            //新的buffer
            pgBufferUsage.shared_blks_written++;
        else if (mode == RBM_NORMAL || mode == RBM_NORMAL_NO_LOG ||
                 mode == RBM_ZERO_ON_ERROR)
            //读取block
            pgBufferUsage.shared_blks_read++;
    }
    
    //这时候,我们还没有持有任何锁.
    
    //---------- 如果buffer已在换冲池中,工作已完成
    if (found)
    {
        //------------- buffer已在缓冲池中
        //已在换冲池中
        if (!isExtend)
        {
            //非扩展buffer
            
            //在退出前,更新统计信息
            *hit = true;
            VacuumPageHit++;
            if (VacuumCostActive)
                VacuumCostBalance += VacuumCostPageHit;
            TRACE_POSTGRESQL_BUFFER_READ_DONE(forkNum, blockNum,
                                              smgr->smgr_rnode.node.spcNode,
                                              smgr->smgr_rnode.node.dbNode,
                                              smgr->smgr_rnode.node.relNode,
                                              smgr->smgr_rnode.backend,
                                              isExtend,
                                              found);
            
            if (!isLocalBuf)
            {
                //非临时表buffer
                if (mode == RBM_ZERO_AND_LOCK)
                    LWLockAcquire(BufferDescriptorGetContentLock(bufHdr),
                                  LW_EXCLUSIVE);
                else if (mode == RBM_ZERO_AND_CLEANUP_LOCK)
                    LockBufferForCleanup(BufferDescriptorGetBuffer(bufHdr));
            }
            //根据buffer描述符读取buffer并返回buffer
            //#define BufferDescriptorGetBuffer(bdesc) ((bdesc)->buf_id + 1)
            return BufferDescriptorGetBuffer(bufHdr);
        }
        
        //获取block
        bufBlock = isLocalBuf ? LocalBufHdrGetBlock(bufHdr) : BufHdrGetBlock(bufHdr);
        if (!PageIsNew((Page) bufBlock))
            //不是PageIsNew,则报错
            ereport(ERROR,
                    (errmsg("unexpected data beyond EOF in block %u of relation %s",
                            blockNum, relpath(smgr->smgr_rnode, forkNum)),
                     errhint("This has been seen to occur with buggy kernels; consider updating your system.")));
        
        if (isLocalBuf)
        {
            //临时表
            
            //只需要调整标记
            uint32      buf_state = pg_atomic_read_u32(&bufHdr->state);
            Assert(buf_state & BM_VALID);
            buf_state &= ~BM_VALID;
            pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
        }
        else
        {
            //非临时表
            
            do
            {
                uint32      buf_state = LockBufHdr(bufHdr);
                Assert(buf_state & BM_VALID);
                //清除BM_VALID标记
                buf_state &= ~BM_VALID;
                UnlockBufHdr(bufHdr, buf_state);
            } while (!StartBufferIO(bufHdr, true));
        }
    }
    //------------- buffer不在缓冲池中
    
    //验证
    Assert(!(pg_atomic_read_u32(&bufHdr->state) & BM_VALID));   
    //获取block
    bufBlock = isLocalBuf ? LocalBufHdrGetBlock(bufHdr) : BufHdrGetBlock(bufHdr);
    if (isExtend)
    {
        //-------- 扩展block
        
        //新buffers使用0填充
        MemSet((char *) bufBlock, 0, BLCKSZ);
        
        //对于使用全0填充的page,不要设置checksum
        smgrextend(smgr, forkNum, blockNum, (char *) bufBlock, false);
        
    }
    else
    {
        //-------- 普通block
        
        if (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK)
            //如为RBM_ZERO_AND_LOCK或者RBM_ZERO_AND_CLEANUP_LOCK模式,初始化为0
            MemSet((char *) bufBlock, 0, BLCKSZ);
        else
        {
            //其他模式
            instr_time  io_start,//io的起止时间
                        io_time;
            if (track_io_timing)
                INSTR_TIME_SET_CURRENT(io_start);
            //smgr(存储管理器)读取block
            smgrread(smgr, forkNum, blockNum, (char *) bufBlock);
            if (track_io_timing)
            {
                //需要跟踪io时间
                INSTR_TIME_SET_CURRENT(io_time);
                INSTR_TIME_SUBTRACT(io_time, io_start);
                pgstat_count_buffer_read_time(INSTR_TIME_GET_MICROSEC(io_time));
                INSTR_TIME_ADD(pgBufferUsage.blk_read_time, io_time);
            }
            
            //检查垃圾数据
            if (!PageIsVerified((Page) bufBlock, blockNum))
            {
                //如果page为通过验证
                if (mode == RBM_ZERO_ON_ERROR || zero_damaged_pages)
                {
                    //出错,则初始化
                    ereport(WARNING,
                            (errcode(ERRCODE_DATA_CORRUPTED),
                             errmsg("invalid page in block %u of relation %s; zeroing out page",
                                    blockNum,
                                    relpath(smgr->smgr_rnode, forkNum))));
                    //初始化
                    MemSet((char *) bufBlock, 0, BLCKSZ);
                }
                else
                    //出错,报错
                    ereport(ERROR,
                            (errcode(ERRCODE_DATA_CORRUPTED),
                             errmsg("invalid page in block %u of relation %s",
                                    blockNum,
                                    relpath(smgr->smgr_rnode, forkNum))));
            }
        }
    }
    //--------- 已扩展了buffer或者已读取了block
    
    if ((mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK) &&
        !isLocalBuf)
    {
        //锁定
        LWLockAcquire(BufferDescriptorGetContentLock(bufHdr), LW_EXCLUSIVE);
    }
    if (isLocalBuf)
    {
        //临时表
        
        //只需要调整标记
        uint32      buf_state = pg_atomic_read_u32(&bufHdr->state);
        buf_state |= BM_VALID;
        pg_atomic_unlocked_write_u32(&bufHdr->state, buf_state);
    }
    else
    {
        //普通表
        
        //设置BM_VALID,中断IO,唤醒等待的进程
        TerminateBufferIO(bufHdr, false, BM_VALID);
    }
    //更新统计信息
    VacuumPageMiss++;
    if (VacuumCostActive)
        VacuumCostBalance += VacuumCostPageMiss;
    //跟踪
    TRACE_POSTGRESQL_BUFFER_READ_DONE(forkNum, blockNum,
                                      smgr->smgr_rnode.node.spcNode,
                                      smgr->smgr_rnode.node.dbNode,
                                      smgr->smgr_rnode.node.relNode,
                                      smgr->smgr_rnode.backend,
                                      isExtend,
                                      found);
    //返回buffer
    //#define BufferDescriptorGetBuffer(bdesc) ((bdesc)->buf_id + 1)
    return BufferDescriptorGetBuffer(bufHdr);
}

三、跟踪分析

测试场景一:Block不在缓冲区中
脚本:

16:42:48 (xdb@[local]:5432)testdb=# select * from t1 limit 10;

启动gdb,设置断点

(gdb) b ReadBuffer_common
Breakpoint 1 at 0x876e28: file bufmgr.c, line 711.
(gdb) c
Continuing.
Breakpoint 1, ReadBuffer_common (smgr=0x2b7cce0, relpersistence=112 'p', forkNum=MAIN_FORKNUM, blockNum=0, mode=RBM_NORMAL, 
    strategy=0x0, hit=0x7ffc7761dfab) at bufmgr.c:711
711     bool        isLocalBuf = SmgrIsTemp(smgr);
(gdb)

1.初始化相关变量和执行相关判断(是否扩展isExtend?是否临时表isLocalBuf?)

(gdb) n
713     *hit = false;
(gdb) 
716     ResourceOwnerEnlargeBuffers(CurrentResourceOwner);
(gdb) 
718     isExtend = (blockNum == P_NEW);
(gdb) 
720     TRACE_POSTGRESQL_BUFFER_READ_START(forkNum, blockNum,
(gdb) 
728     if (isExtend)
(gdb) 
731     if (isLocalBuf)
(gdb) 
745         bufHdr = BufferAlloc(smgr, relpersistence, forkNum, blockNum,
(gdb)

2.调用BufferAlloc获取buffer描述符

(gdb) 
747         if (found)
(gdb) p *bufHdr
$1 = {tag = {rnode = {spcNode = 1663, dbNode = 16402, relNode = 51439}, forkNum = MAIN_FORKNUM, blockNum = 0}, 
  buf_id = 108, state = {value = 2248409089}, wait_backend_pid = 0, freeNext = -2, content_lock = {tranche = 54, state = {
      value = 536870912}, waiters = {head = 2147483647, tail = 2147483647}}}
(gdb) p found
$2 = false
(gdb) 
(gdb) n
750             pgBufferUsage.shared_blks_read++; --> 更新统计信息
(gdb)

4.没有在缓存中命中,则获取block

756     if (found)
(gdb) 
856     Assert(!(pg_atomic_read_u32(&bufHdr->state) & BM_VALID));   
(gdb) 
858     bufBlock = isLocalBuf ? LocalBufHdrGetBlock(bufHdr) : BufHdrGetBlock(bufHdr);
(gdb) 
860     if (isExtend)
(gdb) p bufBlock
$4 = (Block) 0x7fe8c240e380

4.2如为普通buffer
4.2.1如模式为RBM_ZERO_AND_LOCK/RBM_ZERO_AND_CLEANUP_LOCK,填充0
4.2.2否则,通过smgr(存储管理器)读取block,如需要,则跟踪I/O时间,同时检查垃圾数据

(gdb) p mode
$5 = RBM_NORMAL
(gdb) 
(gdb) n
880         if (mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK)
(gdb) 
887             if (track_io_timing)
(gdb) 
890             smgrread(smgr, forkNum, blockNum, (char *) bufBlock);
(gdb) 
892             if (track_io_timing)
(gdb) p *smgr
$6 = {smgr_rnode = {node = {spcNode = 1663, dbNode = 16402, relNode = 51439}, backend = -1}, smgr_owner = 0x7fe8ee2bc7b8, 
  smgr_targblock = 4294967295, smgr_fsm_nblocks = 4294967295, smgr_vm_nblocks = 4294967295, smgr_which = 0, 
  md_num_open_segs = {1, 0, 0, 0}, md_seg_fds = {0x2b0dd78, 0x0, 0x0, 0x0}, next_unowned_reln = 0x0}
(gdb) p forkNum
$7 = MAIN_FORKNUM
(gdb) p blockNum
$8 = 0
(gdb) p (char *) bufBlock
$9 = 0x7fe8c240e380 "\001"
(gdb)

5.已扩展了buffer或者已读取了block
5.1如需要,锁定buffer
5.2如为临时表,则调整标记;否则设置BM_VALID,中断IO,唤醒等待的进程

(gdb) n
901             if (!PageIsVerified((Page) bufBlock, blockNum))
(gdb) 
932     if ((mode == RBM_ZERO_AND_LOCK || mode == RBM_ZERO_AND_CLEANUP_LOCK) &&
(gdb) n
938     if (isLocalBuf)
(gdb) 
949         TerminateBufferIO(bufHdr, false, BM_VALID);
(gdb)

5.3更新统计信息
5.4返回buffer

(gdb) 
952     VacuumPageMiss++;
(gdb) 
953     if (VacuumCostActive)
(gdb) 
956     TRACE_POSTGRESQL_BUFFER_READ_DONE(forkNum, blockNum,
(gdb) 
964     return BufferDescriptorGetBuffer(bufHdr);
(gdb) 
965 }
(gdb)

buf为109

(gdb) 
ReadBufferExtended (reln=0x7fe8ee2bc7a8, forkNum=MAIN_FORKNUM, blockNum=0, mode=RBM_NORMAL, strategy=0x0) at bufmgr.c:666
666     if (hit)
(gdb) 
668     return buf;
(gdb) p buf
$10 = 109
(gdb)

测试场景二:Block已在缓冲区中
再次执行上面的SQL语句,这时候相应的block已读入到buffer中

(gdb) del
Delete all breakpoints? (y or n) y
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
0x00007fe8ec448903 in __epoll_wait_nocancel () at ../sysdeps/unix/syscall-template.S:81
81  T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
(gdb) b ReadBuffer_common
Breakpoint 2 at 0x876e28: file bufmgr.c, line 711.
(gdb)

found变量为T

...
(gdb) 
745         bufHdr = BufferAlloc(smgr, relpersistence, forkNum, blockNum,
(gdb) 
747         if (found)
(gdb) p found
$11 = true
(gdb) 
(gdb) n
748             pgBufferUsage.shared_blks_hit++;
(gdb)

进入相应的逻辑
3.如在缓存中命中
3.1如非扩展buffer,更新统计信息,如有需要,锁定buffer并返回
3.2如为扩展buffer,则获取block
3.2.1如PageIsNew返回F,则报错
3.2.2如为本地buffer(临时表),则调整标记
3.2.3如非本地buffer,则清除BM_VALID标记

(gdb) 
756     if (found)
(gdb) 
758         if (!isExtend)
(gdb) 
761             *hit = true;
(gdb) 
762             VacuumPageHit++;
(gdb) 
764             if (VacuumCostActive)
(gdb) 
767             TRACE_POSTGRESQL_BUFFER_READ_DONE(forkNum, blockNum,
(gdb) 
779             if (!isLocalBuf)
(gdb) 
781                 if (mode == RBM_ZERO_AND_LOCK)
(gdb) 
784                 else if (mode == RBM_ZERO_AND_CLEANUP_LOCK)
(gdb) 
788             return BufferDescriptorGetBuffer(bufHdr);
(gdb) 
965 }
(gdb)

到此,关于“PostgreSQL中ReadBuffer_common函数有什么作用”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-数据库
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯