文章详情

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

请输入下面的图形验证码

提交验证

短信预约提醒成功

PostgreSQL中RecordAndGetPageWithFreeSpace有什么作用

2024-04-02 19:55

关注

本篇内容介绍了“PostgreSQL中RecordAndGetPageWithFreeSpace有什么作用”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

一、数据结构

FSMAddress
内部的FSM处理过程以逻辑地址scheme的方式工作,树的每一个层次都可以认为是一个独立的地址文件.


typedef struct
{
    //层次
    int         level;          
    //该层次内的页编号
    int         logpageno;      
} FSMAddress;

//根页地址
static const FSMAddress FSM_ROOT_ADDRESS = {FSM_ROOT_LEVEL, 0};

FSMPage
FSM page数据结构.详细可参看src/backend/storage/freespace/README.


typedef struct
{
    
    int         fp_next_slot;
    
    uint8       fp_nodes[FLEXIBLE_ARRAY_MEMBER];
} FSMPageData;
typedef FSMPageData *FSMPage;

FSMLocalMap
对于小表,不需要创建FSM来存储空间信息,使用本地的内存映射信息.


//已尝试或者已在表的末尾之后
#define FSM_LOCAL_NOT_AVAIL 0x00

//可用于尝试
#define FSM_LOCAL_AVAIL     0x01

typedef struct
{
    BlockNumber nblocks;//块数
    uint8       map[HEAP_FSM_CREATION_THRESHOLD];//数组
}           FSMLocalMap;
static FSMLocalMap fsm_local_map =
{
    0,
    {
        FSM_LOCAL_NOT_AVAIL
    }
};
#define FSM_LOCAL_MAP_EXISTS (fsm_local_map.nblocks > 0)

二、源码解读

RecordAndGetPageWithFreeSpace返回满足条件的block,其主要逻辑如下:
1.初始化相关变量
2.如存在本地map,则首先使用该文件,调用fsm_local_search
3.如果没有本地map也没有FSM,创建本地map,然后调用fsm_local_search
4.使用FSM搜索
4.1获取FSM中原page可用空间对应的catalog
4.2根据所需空间大小,获取FSM中相应的catalog
4.3根据原页面,获取heap block所在的位置(FSMAddress)
4.4检索获取目标slot
4.5如目标slot合法,则获取相应的block,否则使用fsm_search搜索合适的block


BlockNumber
RecordAndGetPageWithFreeSpace(Relation rel, BlockNumber oldPage,
                              Size oldSpaceAvail, Size spaceNeeded)
{
    int         old_cat;
    int         search_cat;
    FSMAddress  addr;//FSM地址
    uint16      slot;//槽号
    int         search_slot;
    BlockNumber nblocks = InvalidBlockNumber;
    
    //如存在本地map,则首先使用该文件.
    //#define FSM_LOCAL_MAP_EXISTS (fsm_local_map.nblocks > 0)
    if (FSM_LOCAL_MAP_EXISTS)
    {
        Assert((rel->rd_rel->relkind == RELKIND_RELATION ||
                rel->rd_rel->relkind == RELKIND_TOASTVALUE) &&
               fsm_local_map.map[oldPage] == FSM_LOCAL_AVAIL);
        //设置oldPage为不可用
        fsm_local_map.map[oldPage] = FSM_LOCAL_NOT_AVAIL;
        //搜索并返回结果
        return fsm_local_search();
    }
    if (!fsm_allow_writes(rel, oldPage, InvalidBlockNumber, &nblocks))
    {
        //---- 如果FSM不允许写
        
        //设置本地map
        fsm_local_set(rel, nblocks);
        //搜索本地map
        return fsm_local_search();
    }
    
    //------ 使用FSM的逻辑
    //oldSpaceAvail/32,最大255/254
    old_cat = fsm_space_avail_to_cat(oldSpaceAvail);
    //(needed + FSM_CAT_STEP - 1) / FSM_CAT_STEP
    //#define FSM_CAT_STEP   (BLCKSZ / FSM_CATEGORIES)
    //#define FSM_CATEGORIES   256
    search_cat = fsm_space_needed_to_cat(spaceNeeded);
    
    //获得对应heap block的位置
    addr = fsm_get_location(oldPage, &slot);
    //在给定的FSM page和slot中设置值,并返回slot
    search_slot = fsm_set_and_search(rel, addr, slot, old_cat, search_cat);
    
    if (search_slot != -1)
        return fsm_get_heap_blk(addr, search_slot);
    else
        return fsm_search(rel, search_cat);
}
 
static BlockNumber
fsm_local_search(void)
{
    BlockNumber target_block;
    
    //现在本地map必须已设置
    Assert(FSM_LOCAL_MAP_EXISTS);
    //目标block
    target_block = fsm_local_map.nblocks;
    do
    {
        //循环
        target_block--;//从最后一个block开始
        if (fsm_local_map.map[target_block] == FSM_LOCAL_AVAIL)
            return target_block;//最后一个block可用,则返回
    } while (target_block > 0);
    //target_block == 0
    
    FSMClearLocalMap();
    //返回InvalidBlockNumber
    return InvalidBlockNumber;
}

static void
fsm_local_set(Relation rel, BlockNumber cur_nblocks)
{
    BlockNumber blkno,
                cached_target_block;
    
    //验证
    Assert(!FSM_LOCAL_MAP_EXISTS);
    
    blkno = cur_nblocks - 1;//最后一个块
    while (true)
    {
        //更新为可用
        fsm_local_map.map[blkno] = FSM_LOCAL_AVAIL;
        if (blkno >= 2)
            blkno -= 2;
        else
            break;
    }
    
    //缓存块数
    fsm_local_map.nblocks = cur_nblocks;
    
    //设置缓存的目标块状态为未可用
    cached_target_block = RelationGetTargetBlock(rel);
    if (cached_target_block != InvalidBlockNumber &&
        cached_target_block < cur_nblocks)
        fsm_local_map.map[cached_target_block] = FSM_LOCAL_NOT_AVAIL;
}

static uint8
fsm_space_avail_to_cat(Size avail)
{
    int         cat;
    //确保请求的小于块大小
    Assert(avail < BLCKSZ);
    //如大于最大请求大小,返回255
    //#define MaxFSMRequestSize   MaxHeapTupleSize
    //#define MaxHeapTupleSize   (BLCKSZ - MAXALIGN(SizeOfPageHeaderData + sizeof(ItemIdData)))
    if (avail >= MaxFSMRequestSize)
        return 255;
    //#define FSM_CAT_STEP   (BLCKSZ / FSM_CATEGORIES)
    //#define FSM_CATEGORIES   256
    //块大小为8K则FSM_CAT_STEP = 32
    cat = avail / FSM_CAT_STEP;
    
    if (cat > 254)
        cat = 254;//返回254
    return (uint8) cat;
}

static uint8
fsm_space_needed_to_cat(Size needed)
{
    int         cat;
    
    //不能要求最大目录可能表示的空间大小
    if (needed > MaxFSMRequestSize)
        elog(ERROR, "invalid FSM request size %zu", needed);
    if (needed == 0)
        return 1;
    cat = (needed + FSM_CAT_STEP - 1) / FSM_CAT_STEP;
    if (cat > 255)
        cat = 255;
    return (uint8) cat;
}

//addr = fsm_get_location(oldPage, &slot);
static FSMAddress
fsm_get_location(BlockNumber heapblk, uint16 *slot)
{
    FSMAddress  addr;
    addr.level = FSM_BOTTOM_LEVEL;
    //#define SlotsPerFSMPage   LeafNodesPerPage
    //#define LeafNodesPerPage   (NodesPerPage - NonLeafNodesPerPage)
    //#define NodesPerPage (BLCKSZ - MAXALIGN(SizeOfPageHeaderData) - \
                       offsetof(FSMPageData, fp_nodes))
    //#define NonLeafNodesPerPage (BLCKSZ / 2 - 1)
    addr.logpageno = heapblk / SlotsPerFSMPage;
    *slot = heapblk % SlotsPerFSMPage;
    return addr;
}

三、跟踪分析

测试脚本

15:54:13 (xdb@[local]:5432)testdb=# insert into t1 values (1,'1','1');

启动gdb,设置断点

(gdb) b RecordAndGetPageWithFreeSpace
Breakpoint 1 at 0x8879e4: file freespace.c, line 152.
(gdb) c
Continuing.
Breakpoint 1, RecordAndGetPageWithFreeSpace (rel=0x7fad0df13788, oldPage=1, oldSpaceAvail=16, spaceNeeded=32)
    at freespace.c:152
152     int         old_cat = fsm_space_avail_to_cat(oldSpaceAvail);
(gdb)

输入参数

(gdb) p *rel
$5 = {rd_node = {spcNode = 1663, dbNode = 16402, relNode = 50820}, rd_smgr = 0x2084b00, rd_refcnt = 1, rd_backend = -1, 
  rd_islocaltemp = false, rd_isnailed = false, rd_isvalid = true, rd_indexvalid = 1 '\001', rd_statvalid = false, 
  rd_createSubid = 0, rd_newRelfilenodeSubid = 0, rd_rel = 0x7fad0df139a0, rd_att = 0x7fad0df13ab8, rd_id = 50820, 
  rd_lockInfo = {lockRelId = {relId = 50820, dbId = 16402}}, rd_rules = 0x0, rd_rulescxt = 0x0, trigdesc = 0x0, 
  rd_rsdesc = 0x0, rd_fkeylist = 0x0, rd_fkeyvalid = false, rd_partkeycxt = 0x0, rd_partkey = 0x0, rd_pdcxt = 0x0, 
  rd_partdesc = 0x0, rd_partcheck = 0x0, rd_indexlist = 0x7fad0df12820, rd_oidindex = 0, rd_pkindex = 0, 
  rd_replidindex = 0, rd_statlist = 0x0, rd_indexattr = 0x0, rd_projindexattr = 0x0, rd_keyattr = 0x0, rd_pkattr = 0x0, 
  rd_idattr = 0x0, rd_projidx = 0x0, rd_pubactions = 0x0, rd_options = 0x0, rd_index = 0x0, rd_indextuple = 0x0, 
  rd_amhandler = 0, rd_indexcxt = 0x0, rd_amroutine = 0x0, rd_opfamily = 0x0, rd_opcintype = 0x0, rd_support = 0x0, 
  rd_supportinfo = 0x0, rd_indoption = 0x0, rd_indexprs = 0x0, rd_indpred = 0x0, rd_exclops = 0x0, rd_exclprocs = 0x0, 
  rd_exclstrats = 0x0, rd_amcache = 0x0, rd_indcollation = 0x0, rd_fdwroutine = 0x0, rd_toastoid = 0, 
  pgstat_info = 0x20785f0}
(gdb)

1.初始化相关变量
2.如存在本地map,则首先使用该文件,调用fsm_local_search
3.如果没有本地map也没有FSM,创建本地map,然后调用fsm_local_search
4.使用FSM搜索
4.1获取FSM中原page可用空间对应的catalog —> 0
4.2根据所需空间大小,获取FSM中相应的catalog —> 1

(gdb) n
153     int         search_cat = fsm_space_needed_to_cat(spaceNeeded);
(gdb) 
159     addr = fsm_get_location(oldPage, &slot);
(gdb) p old_cat
$1 = 0
(gdb) p search_cat
$2 = 1
(gdb)

4.3根据原页面,获取heap block所在的位置(FSMAddress)

(gdb) n
161     search_slot = fsm_set_and_search(rel, addr, slot, old_cat, search_cat);
(gdb) p addr
$3 = {level = 0, logpageno = 0}
(gdb)

4.4检索获取目标slot

(gdb) n
167     if (search_slot != -1)
(gdb) p search_slot
$4 = 4
(gdb)

4.5如目标slot合法,则获取相应的block,否则使用fsm_search搜索合适的block

(gdb) n
168         return fsm_get_heap_blk(addr, search_slot);
(gdb) 
171 }
(gdb) 
RelationGetBufferForTuple (relation=0x7fad0df13788, len=32, otherBuffer=0, options=0, bistate=0x0, vmbuffer=0x7ffe1b797dcc, 
    vmbuffer_other=0x0) at hio.c:397
397     while (targetBlock != InvalidBlockNumber)
(gdb) p targetBlock
$6 = 4
(gdb)

“PostgreSQL中RecordAndGetPageWithFreeSpace有什么作用”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

阅读原文内容投诉

免责声明:

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

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

软考中级精品资料免费领

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

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

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

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

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

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

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