溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

PostgreSQL中RecordAndGetPageWithFreeSpace有什么作用

發(fā)布時(shí)間:2021-11-09 15:30:20 來(lái)源:億速云 閱讀:128 作者:iii 欄目:關(guān)系型數(shù)據(jù)庫(kù)

本篇內(nèi)容介紹了“PostgreSQL中RecordAndGetPageWithFreeSpace有什么作用”的有關(guān)知識(shí),在實(shí)際案例的操作過(guò)程中,不少人都會(huì)遇到這樣的困境,接下來(lái)就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

一、數(shù)據(jù)結(jié)構(gòu)

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í)用文章!

向AI問(wèn)一下細(xì)節(jié)

免責(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)容。

AI