您好,登錄后才能下訂單哦!
本篇內(nèi)容介紹了“PostgreSQL中RecordAndGetPageWithFreeSpace有什么作用”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!
FSMAddress
內(nèi)部的FSM處理過(guò)程以邏輯地址scheme的方式工作,樹(shù)的每一個(gè)層次都可以認(rèn)為是一個(gè)獨(dú)立的地址文件.
/* * The internal FSM routines work on a logical addressing scheme. Each * level of the tree can be thought of as a separately addressable file. * 內(nèi)部的FSM處理過(guò)程工作在一個(gè)邏輯地址scheme上. * 樹(shù)的每一個(gè)層次都可以認(rèn)為是一個(gè)獨(dú)立的地址文件. */ typedef struct { //層次 int level; /* level */ //該層次內(nèi)的頁(yè)編號(hào) int logpageno; /* page number within the level */ } FSMAddress; /* Address of the root page. */ //根頁(yè)地址 static const FSMAddress FSM_ROOT_ADDRESS = {FSM_ROOT_LEVEL, 0};
FSMPage
FSM page數(shù)據(jù)結(jié)構(gòu).詳細(xì)可參看src/backend/storage/freespace/README.
/* * Structure of a FSM page. See src/backend/storage/freespace/README for * details. * FSM page數(shù)據(jù)結(jié)構(gòu).詳細(xì)可參看src/backend/storage/freespace/README. */ typedef struct { /* * fsm_search_avail() tries to spread the load of multiple backends by * returning different pages to different backends in a round-robin * fashion. fp_next_slot points to the next slot to be returned (assuming * there's enough space on it for the request). It's defined as an int, * because it's updated without an exclusive lock. uint16 would be more * appropriate, but int is more likely to be atomically * fetchable/storable. * fsm_search_avail()函數(shù)嘗試通過(guò)在一輪循環(huán)中返回不同的頁(yè)面到不同的后臺(tái)進(jìn)程, * 從而分散在后臺(tái)進(jìn)程上分散負(fù)載. * 該字段因?yàn)闊o(wú)需獨(dú)占鎖,因此定義為整型. * unit16可能會(huì)更合適,但整型看起來(lái)更適合于原子提取和存儲(chǔ). */ int fp_next_slot; /* * fp_nodes contains the binary tree, stored in array. The first * NonLeafNodesPerPage elements are upper nodes, and the following * LeafNodesPerPage elements are leaf nodes. Unused nodes are zero. * fp_nodes以數(shù)組的形式存儲(chǔ)二叉樹(shù). * 第一個(gè)NonLeafNodesPerPage元素是上一層的節(jié)點(diǎn),接下來(lái)的LeafNodesPerPage元素是葉子節(jié)點(diǎn). * 未使用的節(jié)點(diǎn)為0. */ uint8 fp_nodes[FLEXIBLE_ARRAY_MEMBER]; } FSMPageData; typedef FSMPageData *FSMPage;
FSMLocalMap
對(duì)于小表,不需要?jiǎng)?chuàng)建FSM來(lái)存儲(chǔ)空間信息,使用本地的內(nèi)存映射信息.
/* Either already tried, or beyond the end of the relation */ //已嘗試或者已在表的末尾之后 #define FSM_LOCAL_NOT_AVAIL 0x00 /* Available to try */ //可用于嘗試 #define FSM_LOCAL_AVAIL 0x01 /* * For small relations, we don't create FSM to save space, instead we use * local in-memory map of pages to try. To locate free space, we simply try * pages directly without knowing ahead of time how much free space they have. * 對(duì)于小表,不需要?jiǎng)?chuàng)建FSM來(lái)存儲(chǔ)空間信息,使用本地的內(nèi)存映射信息. * 為了定位空閑空間,我們不需要知道他們有多少空閑空間而是直接簡(jiǎn)單的對(duì)page進(jìn)行嘗試. * * Note that this map is used to the find the block with required free space * for any given relation. We clear this map when we have found a block with * enough free space, when we extend the relation, or on transaction abort. * See src/backend/storage/freespace/README for further details. * 注意這個(gè)map用于搜索給定表的請(qǐng)求空閑空間. * 在找到有足夠空閑空間的block/擴(kuò)展了relation/在事務(wù)回滾時(shí),則清除這個(gè)map的信息. * 詳細(xì)可查看src/backend/storage/freespace/README. */ typedef struct { BlockNumber nblocks;//塊數(shù) uint8 map[HEAP_FSM_CREATION_THRESHOLD];//數(shù)組 } FSMLocalMap; static FSMLocalMap fsm_local_map = { 0, { FSM_LOCAL_NOT_AVAIL } }; #define FSM_LOCAL_MAP_EXISTS (fsm_local_map.nblocks > 0)
RecordAndGetPageWithFreeSpace返回滿足條件的block,其主要邏輯如下:
1.初始化相關(guān)變量
2.如存在本地map,則首先使用該文件,調(diào)用fsm_local_search
3.如果沒(méi)有本地map也沒(méi)有FSM,創(chuàng)建本地map,然后調(diào)用fsm_local_search
4.使用FSM搜索
4.1獲取FSM中原page可用空間對(duì)應(yīng)的catalog
4.2根據(jù)所需空間大小,獲取FSM中相應(yīng)的catalog
4.3根據(jù)原頁(yè)面,獲取heap block所在的位置(FSMAddress)
4.4檢索獲取目標(biāo)slot
4.5如目標(biāo)slot合法,則獲取相應(yīng)的block,否則使用fsm_search搜索合適的block
/* * RecordAndGetPageWithFreeSpace - update info about a page and try again. * RecordAndGetPageWithFreeSpace - 更新page info并再次嘗試. * * We provide this combo form to save some locking overhead, compared to * separate RecordPageWithFreeSpace + GetPageWithFreeSpace calls. There's * also some effort to return a page close to the old page; if there's a * page with enough free space on the same FSM page where the old one page * is located, it is preferred. * 相對(duì)于單獨(dú)的RecordPageWithFreeSpace + GetPageWithFreeSpace調(diào)用, * 我們提供這個(gè)組合形式用于節(jié)省一些鎖的負(fù)載. * 這里同樣存儲(chǔ)一些努力用于返回接近舊page的page. * 如果與舊的page在同一個(gè)FSM page上有足夠空閑空間的page存在,那這個(gè)page會(huì)被選中. * * For very small heap relations that don't have a FSM, we update the local * map to indicate we have tried a page, and return the next page to try. * 對(duì)于非常小的堆表,是不需要FSM的,直接更新本地map來(lái)提示進(jìn)程需要嘗試獲得一個(gè)page,并返回下一個(gè)page. */ BlockNumber RecordAndGetPageWithFreeSpace(Relation rel, BlockNumber oldPage, Size oldSpaceAvail, Size spaceNeeded) { int old_cat; int search_cat; FSMAddress addr;//FSM地址 uint16 slot;//槽號(hào) int search_slot; BlockNumber nblocks = InvalidBlockNumber; /* First try the local map, if it exists. */ //如存在本地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); //設(shè)置oldPage為不可用 fsm_local_map.map[oldPage] = FSM_LOCAL_NOT_AVAIL; //搜索并返回結(jié)果 return fsm_local_search(); } if (!fsm_allow_writes(rel, oldPage, InvalidBlockNumber, &nblocks)) { //---- 如果FSM不允許寫(xiě) /* * If we have neither a local map nor a FSM, we probably just tried * the target block in the smgr relation entry and failed, so we'll * need to create the local map. * 如果沒(méi)有本地map也沒(méi)有FSM, * 那么我們只是嘗試了smgr relation中的目標(biāo)block而且失敗了,那么需要?jiǎng)?chuàng)建本地map. */ //設(shè)置本地map fsm_local_set(rel, nblocks); //搜索本地map return fsm_local_search(); } /* Normal FSM logic follows */ //------ 使用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); /* Get the location of the FSM byte representing the heap block */ //獲得對(duì)應(yīng)heap block的位置 addr = fsm_get_location(oldPage, &slot); //在給定的FSM page和slot中設(shè)置值,并返回slot search_slot = fsm_set_and_search(rel, addr, slot, old_cat, search_cat); /* * If fsm_set_and_search found a suitable new block, return that. * Otherwise, search as usual. * 如fsm_set_and_search成功找到合適的block,則返回;否則,執(zhí)行常規(guī)的檢索. */ if (search_slot != -1) return fsm_get_heap_blk(addr, search_slot); else return fsm_search(rel, search_cat); } /* * Search the local map for an available block to try, in descending order. * As such, there is no heuristic available to decide which order will be * better to try, but the probability of having space in the last block in the * map is higher because that is the most recent block added to the heap. * 以倒序的方式檢索本地map找可用的block. * 在這種情況下,沒(méi)有特別好的辦法用于確定那種排序方法更好, * 但在map中最后一個(gè)block中存在空閑空間的可能性更高,因?yàn)檫@是最近添加到堆中的block. * * This function is used when there is no FSM. * 如無(wú)FSM則使用該函數(shù). */ static BlockNumber fsm_local_search(void) { BlockNumber target_block; /* Local map must be set by now. */ //現(xiàn)在本地map必須已設(shè)置 Assert(FSM_LOCAL_MAP_EXISTS); //目標(biāo)block target_block = fsm_local_map.nblocks; do { //循環(huán) target_block--;//從最后一個(gè)block開(kāi)始 if (fsm_local_map.map[target_block] == FSM_LOCAL_AVAIL) return target_block;//最后一個(gè)block可用,則返回 } while (target_block > 0); //target_block == 0 /* * If we didn't find any available block to try in the local map, then * clear it. This prevents us from using the map again without setting it * first, which would otherwise lead to the same conclusion again and * again. * 在本地map中沒(méi)有發(fā)現(xiàn)可用的block,則清除相關(guān)信息. * 這可以防止我們?cè)跊](méi)有正確設(shè)置map的情況下使用該map, * 這會(huì)導(dǎo)致重復(fù)的相同結(jié)論(沒(méi)有可用的block). */ FSMClearLocalMap(); //返回InvalidBlockNumber return InvalidBlockNumber; } /* * Initialize or update the local map of blocks to try, for when there is * no FSM. * 如無(wú)FSM,則初始化并更新本地map * * When we initialize the map, the whole heap is potentially available to * try. Testing revealed that trying every block can cause a small * performance dip compared to when we use a FSM, so we try every other * block instead. * 在我們初始化map的時(shí)候,整個(gè)堆可能已可用. * 測(cè)試表名,與使用FSM相比,嘗試每個(gè)塊會(huì)導(dǎo)致小幅的性能下降,因此嘗試每一個(gè)塊. */ static void fsm_local_set(Relation rel, BlockNumber cur_nblocks) { BlockNumber blkno, cached_target_block; /* The local map must not be set already. */ //驗(yàn)證 Assert(!FSM_LOCAL_MAP_EXISTS); /* * Starting at the current last block in the relation and working * backwards, mark alternating blocks as available. * 在關(guān)系的當(dāng)前最后一個(gè)塊開(kāi)始往后減少,標(biāo)記可更新的塊可用. */ blkno = cur_nblocks - 1;//最后一個(gè)塊 while (true) { //更新為可用 fsm_local_map.map[blkno] = FSM_LOCAL_AVAIL; if (blkno >= 2) blkno -= 2; else break; } /* Cache the number of blocks. */ //緩存塊數(shù) fsm_local_map.nblocks = cur_nblocks; /* Set the status of the cached target block to 'unavailable'. */ //設(shè)置緩存的目標(biāo)塊狀態(tài)為未可用 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; } /* * Return category corresponding x bytes of free space * 返回相應(yīng)有x字節(jié)空間空間的目錄 */ static uint8 fsm_space_avail_to_cat(Size avail) { int cat; //確保請(qǐng)求的小于塊大小 Assert(avail < BLCKSZ); //如大于最大請(qǐng)求大小,返回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; /* * The highest category, 255, is reserved for MaxFSMRequestSize bytes or * more. * 最高層的目錄,255,保留用于MaxFSMRequestSize或者更大的大小. */ if (cat > 254) cat = 254;//返回254 return (uint8) cat; } /* * Which category does a page need to have, to accommodate x bytes of data? * While fsm_size_to_avail_cat() rounds down, this needs to round up. * 哪一個(gè)目錄有需要的page,可滿足x bytes大小的數(shù)據(jù). * 因?yàn)閒sm_size_to_avail_cat()往下取整,因此這里需要往上取整. */ static uint8 fsm_space_needed_to_cat(Size needed) { int cat; /* Can't ask for more space than the highest category represents */ //不能要求最大目錄可能表示的空間大小 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; } /* * Return the FSM location corresponding to given heap block. * 返回給定堆block的FSM位置. */ //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; }
測(cè)試腳本
15:54:13 (xdb@[local]:5432)testdb=# insert into t1 values (1,'1','1');
啟動(dòng)gdb,設(shè)置斷點(diǎn)
(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)
輸入?yún)?shù)
(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.初始化相關(guān)變量
2.如存在本地map,則首先使用該文件,調(diào)用fsm_local_search
3.如果沒(méi)有本地map也沒(méi)有FSM,創(chuàng)建本地map,然后調(diào)用fsm_local_search
4.使用FSM搜索
4.1獲取FSM中原page可用空間對(duì)應(yīng)的catalog —> 0
4.2根據(jù)所需空間大小,獲取FSM中相應(yīng)的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根據(jù)原頁(yè)面,獲取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檢索獲取目標(biāo)slot
(gdb) n 167 if (search_slot != -1) (gdb) p search_slot $4 = 4 (gdb)
4.5如目標(biāo)slot合法,則獲取相應(yīng)的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有什么作用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識(shí)可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。