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