溫馨提示×

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

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

PostgreSQL中mdread函數(shù)有什么作用

發(fā)布時(shí)間:2021-11-09 15:27:19 來源:億速云 閱讀:161 作者:iii 欄目:關(guān)系型數(shù)據(jù)庫

本篇內(nèi)容主要講解“PostgreSQL中mdread函數(shù)有什么作用”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“PostgreSQL中mdread函數(shù)有什么作用”吧!

PostgreSQL存儲(chǔ)管理的mdread函數(shù)是magnetic disk存儲(chǔ)管理中負(fù)責(zé)讀取的函數(shù).

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

smgrsw
f_smgr函數(shù)指針結(jié)構(gòu)體定義了獨(dú)立的存儲(chǔ)管理模塊和smgr.c之間的API函數(shù).
md是magnetic disk的縮寫.
除了md,先前PG還支持Sony WORM optical disk jukebox and persistent main memory這兩種存儲(chǔ)方式,
但在后面只剩下magnetic disk,其余的已被廢棄不再支持.
“magnetic disk”本身的名稱也存在誤導(dǎo),實(shí)際上md可以支持操作系統(tǒng)提供標(biāo)準(zhǔn)文件系統(tǒng)的任何類型的設(shè)備.

 /*
 * This struct of function pointers defines the API between smgr.c and
 * any individual storage manager module.  Note that smgr subfunctions are
 * generally expected to report problems via elog(ERROR).  An exception is
 * that smgr_unlink should use elog(WARNING), rather than erroring out,
 * because we normally unlink relations during post-commit/abort cleanup,
 * and so it's too late to raise an error.  Also, various conditions that
 * would normally be errors should be allowed during bootstrap and/or WAL
 * recovery --- see comments in md.c for details.
 * 函數(shù)指針結(jié)構(gòu)體定義了獨(dú)立的存儲(chǔ)管理模塊和smgr.c之間的API函數(shù).
 * 注意smgr子函數(shù)通常會(huì)通過elog(ERROR)報(bào)告錯(cuò)誤.
 * 其中一個(gè)例外是smgr_unlink應(yīng)該使用elog(WARNING),而不是把錯(cuò)誤拋出,
 *   因?yàn)橥ㄟ^來說在事務(wù)提交/回滾清理期間才會(huì)解鏈接(unlinke)關(guān)系,
 *   因此這時(shí)候拋出錯(cuò)誤就顯得太晚了.
 * 同時(shí),在bootstrap和/或WAL恢復(fù)期間,各種可能會(huì)出現(xiàn)錯(cuò)誤的情況也應(yīng)被允許 --- 詳細(xì)可查看md.c中的注釋.
 */
typedef struct f_smgr
{
    void        (*smgr_init) (void);    /* may be NULL */
    void        (*smgr_shutdown) (void);    /* may be NULL */
    void        (*smgr_close) (SMgrRelation reln, ForkNumber forknum);
    void        (*smgr_create) (SMgrRelation reln, ForkNumber forknum,
                                bool isRedo);
    bool        (*smgr_exists) (SMgrRelation reln, ForkNumber forknum);
    void        (*smgr_unlink) (RelFileNodeBackend rnode, ForkNumber forknum,
                                bool isRedo);
    void        (*smgr_extend) (SMgrRelation reln, ForkNumber forknum,
                                BlockNumber blocknum, char *buffer, bool skipFsync);
    void        (*smgr_prefetch) (SMgrRelation reln, ForkNumber forknum,
                                  BlockNumber blocknum);
    void        (*smgr_read) (SMgrRelation reln, ForkNumber forknum,
                              BlockNumber blocknum, char *buffer);
    void        (*smgr_write) (SMgrRelation reln, ForkNumber forknum,
                               BlockNumber blocknum, char *buffer, bool skipFsync);
    void        (*smgr_writeback) (SMgrRelation reln, ForkNumber forknum,
                                   BlockNumber blocknum, BlockNumber nblocks);
    BlockNumber (*smgr_nblocks) (SMgrRelation reln, ForkNumber forknum);
    void        (*smgr_truncate) (SMgrRelation reln, ForkNumber forknum,
                                  BlockNumber nblocks);
    void        (*smgr_immedsync) (SMgrRelation reln, ForkNumber forknum);
    void        (*smgr_pre_ckpt) (void);    /* may be NULL */
    void        (*smgr_sync) (void);    /* may be NULL */
    void        (*smgr_post_ckpt) (void);   /* may be NULL */
} f_smgr;
/*
md是magnetic disk的縮寫.
除了md,先前PG還支持Sony WORM optical disk jukebox and persistent main memory這兩種存儲(chǔ)方式,
但在后面只剩下magnetic disk,其余的已被廢棄不再支持.
"magnetic disk"本身的名稱也存在誤導(dǎo),實(shí)際上md可以支持操作系統(tǒng)提供標(biāo)準(zhǔn)文件系統(tǒng)的任何類型的設(shè)備.
*/
static const f_smgr smgrsw[] = {
    /* magnetic disk */
    {
        .smgr_init = mdinit,
        .smgr_shutdown = NULL,
        .smgr_close = mdclose,
        .smgr_create = mdcreate,
        .smgr_exists = mdexists,
        .smgr_unlink = mdunlink,
        .smgr_extend = mdextend,
        .smgr_prefetch = mdprefetch,
        .smgr_read = mdread,
        .smgr_write = mdwrite,
        .smgr_writeback = mdwriteback,
        .smgr_nblocks = mdnblocks,
        .smgr_truncate = mdtruncate,
        .smgr_immedsync = mdimmedsync,
        .smgr_pre_ckpt = mdpreckpt,
        .smgr_sync = mdsync,
        .smgr_post_ckpt = mdpostckpt
    }
};

MdfdVec
magnetic disk存儲(chǔ)管理在自己的描述符池中跟蹤打開的文件描述符.
之所以這樣做是因?yàn)楸阌谥С殖^os文件大小上限(通常是2GB)的關(guān)系.
為了達(dá)到這個(gè)目的,我們拆分關(guān)系為多個(gè)比OS文件大小上限要小的”segment”文件.
段大小通過pg_config.h中定義的RELSEG_SIZE配置參數(shù)設(shè)置.

/*
 *  The magnetic disk storage manager keeps track of open file
 *  descriptors in its own descriptor pool.  This is done to make it
 *  easier to support relations that are larger than the operating
 *  system's file size limit (often 2GBytes).  In order to do that,
 *  we break relations up into "segment" files that are each shorter than
 *  the OS file size limit.  The segment size is set by the RELSEG_SIZE
 *  configuration constant in pg_config.h.
 *  magnetic disk存儲(chǔ)管理在自己的描述符池中跟蹤打開的文件描述符.
 *  之所以這樣做是因?yàn)楸阌谥С殖^os文件大小上限(通常是2GB)的關(guān)系.
 *  為了達(dá)到這個(gè)目的,我們拆分關(guān)系為多個(gè)比OS文件大小上限要小的"segment"文件.
 *  段大小通過pg_config.h中定義的RELSEG_SIZE配置參數(shù)設(shè)置.
 *
 *  On disk, a relation must consist of consecutively numbered segment
 *  files in the pattern
 *      -- Zero or more full segments of exactly RELSEG_SIZE blocks each
 *      -- Exactly one partial segment of size 0 <= size < RELSEG_SIZE blocks
 *      -- Optionally, any number of inactive segments of size 0 blocks.
 *  The full and partial segments are collectively the "active" segments.
 *  Inactive segments are those that once contained data but are currently
 *  not needed because of an mdtruncate() operation.  The reason for leaving
 *  them present at size zero, rather than unlinking them, is that other
 *  backends and/or the checkpointer might be holding open file references to
 *  such segments.  If the relation expands again after mdtruncate(), such
 *  that a deactivated segment becomes active again, it is important that
 *  such file references still be valid --- else data might get written
 *  out to an unlinked old copy of a segment file that will eventually
 *  disappear.
 *  在磁盤上,關(guān)系必須由按照某種模式連續(xù)編號(hào)的segment files組成.
 *    -- 每個(gè)RELSEG_SIZE塊的另段或多個(gè)完整段
 *    -- 大小滿足0 <= size < RELSEG_SIZE blocks的一個(gè)部分段
 *    -- 可選的,大小為0 blocks的N個(gè)非活動(dòng)段
 *  完整和部分段統(tǒng)稱為活動(dòng)段.非活動(dòng)段指的是哪些因?yàn)閙dtruncate()操作而出現(xiàn)的包含數(shù)據(jù)但目前不需要的.
 *  保留這些大小為0的非活動(dòng)段而不是unlinking的原因是其他進(jìn)程和/或checkpointer進(jìn)程可能
 *    持有這些段的文件依賴.
 *  如果關(guān)系在mdtruncate()之后再次擴(kuò)展了,這樣一個(gè)無效的會(huì)重新變?yōu)榛顒?dòng)段,
 *    因此文件依賴仍然保持有效是很重要的 
 *    --- 否則數(shù)據(jù)可能寫出到未經(jīng)鏈接的舊segment file拷貝上,會(huì)時(shí)不時(shí)的出現(xiàn)數(shù)據(jù)丟失.
 *
 *  File descriptors are stored in the per-fork md_seg_fds arrays inside
 *  SMgrRelation. The length of these arrays is stored in md_num_open_segs.
 *  Note that a fork's md_num_open_segs having a specific value does not
 *  necessarily mean the relation doesn't have additional segments; we may
 *  just not have opened the next segment yet.  (We could not have "all
 *  segments are in the array" as an invariant anyway, since another backend
 *  could extend the relation while we aren't looking.)  We do not have
 *  entries for inactive segments, however; as soon as we find a partial
 *  segment, we assume that any subsequent segments are inactive.
 *  文件描述符在SMgrRelation中的per-fork md_seg_fds數(shù)組存儲(chǔ).
 *  這些數(shù)組的長度存儲(chǔ)在md_num_open_segs中.
 *  注意一個(gè)fork的md_num_open_segs有一個(gè)特定值并不必要意味著關(guān)系不能有額外的段,
 *    我們只是還沒有打開下一個(gè)段而已.
 *  (但不管怎樣,我們不可能把"所有段都放在數(shù)組中"作為一個(gè)不變式看待,
 *   因?yàn)槠渌笈_(tái)進(jìn)程在尚未檢索時(shí)已經(jīng)擴(kuò)展了關(guān)系)
 *  但是,我們不需要持有非活動(dòng)段的條目,只要我們一旦發(fā)現(xiàn)部分段,那么就可以假定接下來的段是非活動(dòng)的.
 *
 *  The entire MdfdVec array is palloc'd in the MdCxt memory context.
 *  整個(gè)MdfdVec數(shù)組通過palloc在MdCxt內(nèi)存上下文中分配.
 */
typedef struct _MdfdVec
{
    //文件描述符池中該文件的編號(hào)
    File        mdfd_vfd;       /* fd number in fd.c's pool */
    //段號(hào),從0起算
    BlockNumber mdfd_segno;     /* segment number, from 0 */
} MdfdVec;

二、源碼解讀

mdread() — 從relation中讀取相應(yīng)的block.
源碼較為簡單,主要是調(diào)用FileRead函數(shù)執(zhí)行實(shí)際的讀取操作.

/*
 *  mdread() -- Read the specified block from a relation.
 *  mdread() -- 從relation中讀取相應(yīng)的block
 */
void
mdread(SMgrRelation reln, ForkNumber forknum, BlockNumber blocknum,
       char *buffer)
{
    off_t       seekpos;//seek的位置
    int         nbytes;//bytes
    MdfdVec    *v;//md文件描述符向量數(shù)組
    TRACE_POSTGRESQL_SMGR_MD_READ_START(forknum, blocknum,
                                        reln->smgr_rnode.node.spcNode,
                                        reln->smgr_rnode.node.dbNode,
                                        reln->smgr_rnode.node.relNode,
                                        reln->smgr_rnode.backend);
    //獲取向量數(shù)組
    v = _mdfd_getseg(reln, forknum, blocknum, false,
                     EXTENSION_FAIL | EXTENSION_CREATE_RECOVERY);
    //獲取block偏移
    seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
    //驗(yàn)證
    Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
    //讀取文件,讀入buffer中,返回讀取的字節(jié)數(shù)
    nbytes = FileRead(v->mdfd_vfd, buffer, BLCKSZ, seekpos, WAIT_EVENT_DATA_FILE_READ);
    //跟蹤
    TRACE_POSTGRESQL_SMGR_MD_READ_DONE(forknum, blocknum,
                                       reln->smgr_rnode.node.spcNode,
                                       reln->smgr_rnode.node.dbNode,
                                       reln->smgr_rnode.node.relNode,
                                       reln->smgr_rnode.backend,
                                       nbytes,
                                       BLCKSZ);
    if (nbytes != BLCKSZ)
    {
        //讀取的字節(jié)數(shù)不等于塊大小,報(bào)錯(cuò)
        if (nbytes < 0)
            ereport(ERROR,
                    (errcode_for_file_access(),
                     errmsg("could not read block %u in file \"%s\": %m",
                            blocknum, FilePathName(v->mdfd_vfd))));
        /*
         * Short read: we are at or past EOF, or we read a partial block at
         * EOF.  Normally this is an error; upper levels should never try to
         * read a nonexistent block.  However, if zero_damaged_pages is ON or
         * we are InRecovery, we should instead return zeroes without
         * complaining.  This allows, for example, the case of trying to
         * update a block that was later truncated away.
         * Short read:處于EOF或者在EOF之后,或者在EOF處讀取了一個(gè)部分塊.
         * 通常來說,這是一個(gè)錯(cuò)誤,高層代碼不應(yīng)嘗試讀取一個(gè)不存在的block.
         *  但是,如果zero_damaged_pages參數(shù)設(shè)置為ON或者處于InRecovery狀態(tài),那么應(yīng)該返回0而不報(bào)錯(cuò).
         * 比如,這可以允許嘗試更新一個(gè)塊但隨后就給截?cái)嗟那闆r.
         */
        if (zero_damaged_pages || InRecovery)
            MemSet(buffer, 0, BLCKSZ);
        else
            ereport(ERROR,
                    (errcode(ERRCODE_DATA_CORRUPTED),
                     errmsg("could not read block %u in file \"%s\": read only %d of %d bytes",
                            blocknum, FilePathName(v->mdfd_vfd),
                            nbytes, BLCKSZ)));
    }
}

三、跟蹤分析

測(cè)試腳本

11:15:11 (xdb@[local]:5432)testdb=# insert into t1(id) select generate_series(100,500);

啟動(dòng)gdb,跟蹤
查看調(diào)用棧

(gdb) b mdread
Breakpoint 3 at 0x8b669b: file md.c, line 738.
(gdb) c
Continuing.
Breakpoint 3, mdread (reln=0x2d09be0, forknum=MAIN_FORKNUM, blocknum=50, buffer=0x7f3823369c00 "") at md.c:738
738     TRACE_POSTGRESQL_SMGR_MD_READ_START(forknum, blocknum,
(gdb) bt
#0  mdread (reln=0x2d09be0, forknum=MAIN_FORKNUM, blocknum=50, buffer=0x7f3823369c00 "") at md.c:738
#1  0x00000000008b92d5 in smgrread (reln=0x2d09be0, forknum=MAIN_FORKNUM, blocknum=50, buffer=0x7f3823369c00 "")
    at smgr.c:628
#2  0x00000000008793f9 in ReadBuffer_common (smgr=0x2d09be0, relpersistence=112 'p', forkNum=MAIN_FORKNUM, blockNum=50, 
    mode=RBM_NORMAL, strategy=0x0, hit=0x7ffd5fb2948b) at bufmgr.c:890
#3  0x0000000000878cd4 in ReadBufferExtended (reln=0x7f3836e1e788, forkNum=MAIN_FORKNUM, blockNum=50, mode=RBM_NORMAL, 
    strategy=0x0) at bufmgr.c:664
#4  0x0000000000878bb1 in ReadBuffer (reln=0x7f3836e1e788, blockNum=50) at bufmgr.c:596
#5  0x00000000004eeb96 in ReadBufferBI (relation=0x7f3836e1e788, targetBlock=50, bistate=0x0) at hio.c:87
#6  0x00000000004ef387 in RelationGetBufferForTuple (relation=0x7f3836e1e788, len=32, otherBuffer=0, options=0, 
    bistate=0x0, vmbuffer=0x7ffd5fb295ec, vmbuffer_other=0x0) at hio.c:415
#7  0x00000000004df1f8 in heap_insert (relation=0x7f3836e1e788, tup=0x2ca6770, cid=0, options=0, bistate=0x0)
    at heapam.c:2468
#8  0x0000000000709dda in ExecInsert (mtstate=0x2ca4c40, slot=0x2ca3418, planSlot=0x2ca3418, estate=0x2ca48d8, 
    canSetTag=true) at nodeModifyTable.c:529
#9  0x000000000070c475 in ExecModifyTable (pstate=0x2ca4c40) at nodeModifyTable.c:2159
#10 0x00000000006e05cb in ExecProcNodeFirst (node=0x2ca4c40) at execProcnode.c:445
#11 0x00000000006d552e in ExecProcNode (node=0x2ca4c40) at ../../../src/include/executor/executor.h:247
#12 0x00000000006d7d66 in ExecutePlan (estate=0x2ca48d8, planstate=0x2ca4c40, use_parallel_mode=false, 
    operation=CMD_INSERT, sendTuples=false, numberTuples=0, direction=ForwardScanDirection, dest=0x2d41a30, 
    execute_once=true) at execMain.c:1723
#13 0x00000000006d5af8 in standard_ExecutorRun (queryDesc=0x2ca24b8, direction=ForwardScanDirection, count=0, 
    execute_once=true) at execMain.c:364
#14 0x00000000006d5920 in ExecutorRun (queryDesc=0x2ca24b8, direction=ForwardScanDirection, count=0, execute_once=true)
    at execMain.c:307
#15 0x00000000008c1092 in ProcessQuery (plan=0x2d418b8, 
    sourceText=0x2c7eec8 "insert into t1(id) select generate_series(100,500);", params=0x0, queryEnv=0x0, dest=0x2d41a30, 
---Type <return> to continue, or q <return> to quit---
    completionTag=0x7ffd5fb29b80 "") at pquery.c:161
#16 0x00000000008c29a1 in PortalRunMulti (portal=0x2ce4488, isTopLevel=true, setHoldSnapshot=false, dest=0x2d41a30, 
    altdest=0x2d41a30, completionTag=0x7ffd5fb29b80 "") at pquery.c:1286
#17 0x00000000008c1f7a in PortalRun (portal=0x2ce4488, count=9223372036854775807, isTopLevel=true, run_once=true, 
    dest=0x2d41a30, altdest=0x2d41a30, completionTag=0x7ffd5fb29b80 "") at pquery.c:799
#18 0x00000000008bbf16 in exec_simple_query (query_string=0x2c7eec8 "insert into t1(id) select generate_series(100,500);")
    at postgres.c:1145
#19 0x00000000008c01a1 in PostgresMain (argc=1, argv=0x2ca8af8, dbname=0x2ca8960 "testdb", username=0x2c7bba8 "xdb")
    at postgres.c:4182
#20 0x000000000081e07c in BackendRun (port=0x2ca0940) at postmaster.c:4361
#21 0x000000000081d7ef in BackendStartup (port=0x2ca0940) at postmaster.c:4033
#22 0x0000000000819be9 in ServerLoop () at postmaster.c:1706
#23 0x000000000081949f in PostmasterMain (argc=1, argv=0x2c79b60) at postmaster.c:1379
#24 0x0000000000742941 in main (argc=1, argv=0x2c79b60) at main.c:228
(gdb)

獲取讀取的偏移

(gdb) n
744     v = _mdfd_getseg(reln, forknum, blocknum, false,
(gdb) 
747     seekpos = (off_t) BLCKSZ * (blocknum % ((BlockNumber) RELSEG_SIZE));
(gdb) p *v
$1 = {mdfd_vfd = 26, mdfd_segno = 0}
(gdb) p BLCKSZ
$2 = 8192
(gdb) p blocknum
$3 = 50
(gdb) p RELSEG_SIZE
$4 = 131072
(gdb) n
749     Assert(seekpos < (off_t) BLCKSZ * RELSEG_SIZE);
(gdb) p seekpos
$5 = 409600
(gdb)

執(zhí)行讀取操作

(gdb) n
751     if (FileSeek(v->mdfd_vfd, seekpos, SEEK_SET) != seekpos)
(gdb) 
757     nbytes = FileRead(v->mdfd_vfd, buffer, BLCKSZ, WAIT_EVENT_DATA_FILE_READ);
(gdb) 
759     TRACE_POSTGRESQL_SMGR_MD_READ_DONE(forknum, blocknum,
(gdb) p nbytes
$6 = 8192
(gdb) p *buffer
$7 = 1 '\001'
(gdb) n
767     if (nbytes != BLCKSZ)
(gdb) 
792 }
(gdb) 
smgrread (reln=0x2d09be0, forknum=MAIN_FORKNUM, blocknum=50, buffer=0x7f3823369c00 "\001") at smgr.c:629
629 }
(gdb)

到此,相信大家對(duì)“PostgreSQL中mdread函數(shù)有什么作用”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!

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

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI