您好,登錄后才能下訂單哦!
本篇內容介紹了“PostgreSQL中RecordAndGetPageWithFreeSpace有什么作用”的有關知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領大家學習一下如何處理這些情況吧!希望大家仔細閱讀,能夠學有所成!
FSMAddress
內部的FSM處理過程以邏輯地址scheme的方式工作,樹的每一個層次都可以認為是一個獨立的地址文件.
/* * The internal FSM routines work on a logical addressing scheme. Each * level of the tree can be thought of as a separately addressable file. * 內部的FSM處理過程工作在一個邏輯地址scheme上. * 樹的每一個層次都可以認為是一個獨立的地址文件. */ typedef struct { //層次 int level; /* level */ //該層次內的頁編號 int logpageno; /* page number within the level */ } FSMAddress; /* Address of the root page. */ //根頁地址 static const FSMAddress FSM_ROOT_ADDRESS = {FSM_ROOT_LEVEL, 0};
FSMPage
FSM page數據結構.詳細可參看src/backend/storage/freespace/README.
/* * Structure of a FSM page. See src/backend/storage/freespace/README for * details. * FSM page數據結構.詳細可參看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()函數嘗試通過在一輪循環中返回不同的頁面到不同的后臺進程, * 從而分散在后臺進程上分散負載. * 該字段因為無需獨占鎖,因此定義為整型. * unit16可能會更合適,但整型看起來更適合于原子提取和存儲. */ 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以數組的形式存儲二叉樹. * 第一個NonLeafNodesPerPage元素是上一層的節點,接下來的LeafNodesPerPage元素是葉子節點. * 未使用的節點為0. */ uint8 fp_nodes[FLEXIBLE_ARRAY_MEMBER]; } FSMPageData; typedef FSMPageData *FSMPage;
FSMLocalMap
對于小表,不需要創建FSM來存儲空間信息,使用本地的內存映射信息.
/* 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. * 對于小表,不需要創建FSM來存儲空間信息,使用本地的內存映射信息. * 為了定位空閑空間,我們不需要知道他們有多少空閑空間而是直接簡單的對page進行嘗試. * * 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. * 注意這個map用于搜索給定表的請求空閑空間. * 在找到有足夠空閑空間的block/擴展了relation/在事務回滾時,則清除這個map的信息. * 詳細可查看src/backend/storage/freespace/README. */ 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
/* * 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. * 相對于單獨的RecordPageWithFreeSpace + GetPageWithFreeSpace調用, * 我們提供這個組合形式用于節省一些鎖的負載. * 這里同樣存儲一些努力用于返回接近舊page的page. * 如果與舊的page在同一個FSM page上有足夠空閑空間的page存在,那這個page會被選中. * * 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. * 對于非常小的堆表,是不需要FSM的,直接更新本地map來提示進程需要嘗試獲得一個page,并返回下一個page. */ 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; /* 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); //設置oldPage為不可用 fsm_local_map.map[oldPage] = FSM_LOCAL_NOT_AVAIL; //搜索并返回結果 return fsm_local_search(); } if (!fsm_allow_writes(rel, oldPage, InvalidBlockNumber, &nblocks)) { //---- 如果FSM不允許寫 /* * 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. * 如果沒有本地map也沒有FSM, * 那么我們只是嘗試了smgr relation中的目標block而且失敗了,那么需要創建本地map. */ //設置本地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 */ //獲得對應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 fsm_set_and_search found a suitable new block, return that. * Otherwise, search as usual. * 如fsm_set_and_search成功找到合適的block,則返回;否則,執行常規的檢索. */ 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. * 在這種情況下,沒有特別好的辦法用于確定那種排序方法更好, * 但在map中最后一個block中存在空閑空間的可能性更高,因為這是最近添加到堆中的block. * * This function is used when there is no FSM. * 如無FSM則使用該函數. */ static BlockNumber fsm_local_search(void) { BlockNumber target_block; /* Local map must be set by now. */ //現在本地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 /* * 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中沒有發現可用的block,則清除相關信息. * 這可以防止我們在沒有正確設置map的情況下使用該map, * 這會導致重復的相同結論(沒有可用的block). */ FSMClearLocalMap(); //返回InvalidBlockNumber return InvalidBlockNumber; } /* * Initialize or update the local map of blocks to try, for when there is * no FSM. * 如無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的時候,整個堆可能已可用. * 測試表名,與使用FSM相比,嘗試每個塊會導致小幅的性能下降,因此嘗試每一個塊. */ static void fsm_local_set(Relation rel, BlockNumber cur_nblocks) { BlockNumber blkno, cached_target_block; /* The local map must not be set already. */ //驗證 Assert(!FSM_LOCAL_MAP_EXISTS); /* * Starting at the current last block in the relation and working * backwards, mark alternating blocks as available. * 在關系的當前最后一個塊開始往后減少,標記可更新的塊可用. */ blkno = cur_nblocks - 1;//最后一個塊 while (true) { //更新為可用 fsm_local_map.map[blkno] = FSM_LOCAL_AVAIL; if (blkno >= 2) blkno -= 2; else break; } /* Cache the number of blocks. */ //緩存塊數 fsm_local_map.nblocks = cur_nblocks; /* Set the status of the cached target block to 'unavailable'. */ //設置緩存的目標塊狀態為未可用 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 * 返回相應有x字節空間空間的目錄 */ 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; /* * 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. * 哪一個目錄有需要的page,可滿足x bytes大小的數據. * 因為fsm_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; }
測試腳本
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有什么作用”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識可以關注億速云網站,小編將為大家輸出更多高質量的實用文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。