Postgres中重要的数据结构

PostgresMain()中重要的几个初始化

关键词:PostgresMain,BaseInit,InitProcess

上次写完了Postmaster 里面的一些内存初始化结构,具体参考:
  1. Postmaster的Shared Memory中的shmem index table 内存结构
  2. Postmaster的Shared Memory中的shared buffer pool内存结构
  3. Postmaster的Memory Context 初始化内存结构
  4. PostmasterMain()中的process table的初始化后内存结构

接下来就得写写Postgres进程里面的初始化内存结构了。如何调试PostgresMain()从 pg_usleep(PostAuthDelay * 1000000L);到 for()循环之间的代码请参考: Postmaster 命令 -W 参数使用中的启动参数说明。

我们知道,一个新的connection来到后,postmaster通过fork()一个postgre进程,PostgresMain()开始做一些初始化,然后进入for(;;)循环,处理后续的sql命令。那么从进入PostgresMain()函数开始到for(;;)循环之间关于该进程有哪些重要的初始化呢?因为我们想知道是否在内存中是否通过初始化给将要buffer的table,heaptuple预留出相应的内存空间。
通过分析,在PostgresMain()中我们发现有三个重要的初始化函数:
  1. BaseInit();
  2. InitProcess();
  3. am_superuser = InitPostgres(dbname, InvalidOid, username, NULL);
1.我们先来看BaseInit();
void BaseInit(void){
    /*
     * Attach to shared memory and semaphores, and initialize our
     * input/output/debugging file descriptors.
     */
    InitCommunication();
    DebugFileOpen();

    /* Do local initialization of file, storage and buffer managers */
    InitFileAccess();
    smgrinit();
    InitBufferPoolAccess();
}
其中 InitCommunication()没做什么事。 DebugFileOpen()暂时我们先不管它。接下来就是 InitFileAccess():
void InitFileAccess(void){
    Assert(SizeVfdCache == 0);    /* call me only once */

    /* initialize cache header entry */
    VfdCache = (Vfd *) malloc(sizeof(Vfd));
    if (VfdCache == NULL)
        ereport(FATAL,
                (errcode(ERRCODE_OUT_OF_MEMORY),
                 errmsg("out of memory")));

    MemSet((char *) &(VfdCache[0]), 0, sizeof(Vfd));
    VfdCache->fd = VFD_CLOSED;

    SizeVfdCache = 1;

    /* register proc-exit hook to ensure temp files are dropped at exit */
    on_proc_exit(AtProcExit_Files, 0);
}
我们发现其实也没做什么工作,主要是初始化了VfdCache和SizeVfdCache。
接下来是 smgrinit(),即初始化硬盘管理器,static const f_smgr smgrsw[],通过跟踪我们发现同样没做什么工作。
那么最后一项 InitBufferPoolAccess()做了哪些工作呢?通过该调用:
void InitBufferPoolAccess(void){
    /*
     * Allocate and zero local arrays of per-buffer info.
     */
    PrivateRefCount = (int32 *) calloc(NBuffers, sizeof(int32));
    if (!PrivateRefCount)
        ereport(FATAL,
                (errcode(ERRCODE_OUT_OF_MEMORY),
                 errmsg("out of memory")));
}
我们发现,只是初始化了NBuffers个PrivateRefCount 。
故,我们小结一下:BaseInit();简单初始化了几个变量:VfdCache ,SizeVfdCache ,PrivateRefCount 。
如下图:


2.接下来我们看看InitProcess()
void InitProcess(void){
    volatile PROC_HDR *procglobal = ProcGlobal;
    int            i;
    if (procglobal == NULL)        elog(PANIC, "proc header uninitialized");
    if (MyProc != NULL)        elog(ERROR, "you already exist");

    SpinLockAcquire(ProcStructLock);
    set_spins_per_delay(procglobal->spins_per_delay);

    if (IsAutoVacuumWorkerProcess())
        MyProc = procglobal->autovacFreeProcs;
    else
        MyProc = procglobal->freeProcs;

    if (MyProc != NULL)    {
        if (IsAutoVacuumWorkerProcess())
            procglobal->autovacFreeProcs = (PGPROC *) MyProc->links.next;
        else
            procglobal->freeProcs = (PGPROC *) MyProc->links.next;
        SpinLockRelease(ProcStructLock);
    }
    else   {
        SpinLockRelease(ProcStructLock);
        ereport(FATAL,
                (errcode(ERRCODE_TOO_MANY_CONNECTIONS),
                 errmsg("sorry, too many clients already")));
    } 
    if (IsUnderPostmaster)
        MarkPostmasterChildActive();

    SHMQueueElemInit(&(MyProc->links));
    MyProc->waitStatus = STATUS_OK;
    MyProc->lxid = InvalidLocalTransactionId;
    MyProc->xid = InvalidTransactionId;
    MyProc->xmin = InvalidTransactionId;
    MyProc->pid = MyProcPid;
    /* backendId, databaseId and roleId will be filled in later */
    MyProc->backendId = InvalidBackendId;
    MyProc->databaseId = InvalidOid;
    MyProc->roleId = InvalidOid;
    MyProc->inCommit = false;
    MyProc->vacuumFlags = 0;
    if (IsAutoVacuumWorkerProcess())
        MyProc->vacuumFlags |= PROC_IS_AUTOVACUUM;
    MyProc->lwWaiting = false;
    MyProc->lwExclusive = false;
    MyProc->lwWaitLink = NULL;
    MyProc->waitLock = NULL;
    MyProc->waitProcLock = NULL;
    for (i = 0; i < NUM_LOCK_PARTITIONS; i++)
        SHMQueueInit(&(MyProc->myProcLocks[i]));

    PGSemaphoreReset(&MyProc->sem);
    on_shmem_exit(ProcKill, 0);
    InitDeadLockChecking();
}
可见,MyProc主要从ProcGlobal->freeProcs列表中找到第一个,赋给自己,然后简单的填充各项数据。
这样,经过第2项的初始化之后,我们得出内存中的数据结构为下图,图中灰色颜色为在heap中,浅黄色表示在shared memory中,红色代表在data段:


3.am_superuser = InitPostgres(dbname, InvalidOid, username, NULL)
进入最复杂的InitPostgres("mydb", InvalidOid, "postgres", NULL)。我们先列列都调用了哪些重要函数:
  • SetDatabasePath(fullpath);
  • InitProcessPhase2();
  • InitBufferPoolBackend();
  • RelationCacheInitialize();
  • InitCatalogCache();
  • InitPlanCache();
  • EnablePortalManager();
  • RelationCacheInitializePhase2();
  • CheckMyDatabase(dbname, am_superuser);
  • InitializeSearchPath();
  • InitializeClientEncoding();
哇哈哈,可真不少,不过一步一步走,总会走完长征路的,先泡杯竹叶青,慢慢品,抗一下辐射,因为下面的路很长、很长。我先把重要的两个函数用下划线标出来,让你先知道哪个是重点。首先声明,看完这部分是有好处的,就是你很快就会发现你能解读生成QueryTree过程中的所有相关代码了。绝不是“辛辛苦苦几十年,一夜回到解放前”。

好,开工,先来看: SetDatabasePath(fullpath);
这个最简单,就是调用 DatabasePath = strdup(path); 设置 DatabasePath 全局变量,为:"base/16384",这里的16384是Oid    MyDatabaseId =16384。怎么知道MyDatabaseId 的呢,实际上实现很简单,就是直接读取文件/usr/local/pgsql/data/global/pg_database 把有"mydb"那一行找出来,解析一下就是了。我的pg_database示例如下:
"template1" 1 1663 648
"template0" 11563 1663 648
"postgres" 11564 1663 648
"mydb" 16384 1663 648
"test3" 17240 1663 648

接下来是 InitProcessPhase2(); 还记得第二步当中的 InitProcess()吗?那里几乎只是简单的初始化了一下,这里要继续初始化什么呢?先看调用这个函数的注释:
/*
     * Finish filling in the PGPROC struct, and add it to the ProcArray. (We
     * need to know MyDatabaseId before we can do this, since it's entered
     * into the PGPROC struct.)
     *
     * Once I have done this, I am visible to other backends!
     */
看样子也没干什么大不了的事,进到函数里面看看,果然如此:
先是MyProc->databaseId = MyDatabaseId;   赋值16384。
接着ProcArrayAdd(MyProc);。里面几乎也没干什么事,只是设置了全局变量procArray->procs[arrayP->numProcs] = proc;
到这里我们画张图看看,内存里都有了啥:


看样子上面没干什么复杂的东西,接下来分析分析 InitBufferPoolBackend();
这里很简单就是注册了一个回调函数 on_shmem_exit(AtProcExit_Buffers, 0);

接下来就是 RelationCacheInitialize();
该函数只是在heap上创建了一个用于relation查找的hash表。我们在postmaster的初始化里对hash表数据结构已经很熟悉了,只是这里创建在heap上,而不是shared memory里,该RealtionIdCache内存结构如下图。
一点说明:有些人会问,这个RealtionIdCache是干什么用的呢?试想一下,我们数据库中有很多relation,这些relation如果要从硬盘上搬到内存的话,肯定是在内存中要有个地方放该relation的实际数据的,例如可能访问该relation的第一个page,放这个实际数据的位置就是postmaster中初始化的BLOCK数据结构,那么肯定有一个数据结构来描述这个打开的relation的,这个数据结构是什么呢,就是Relation,那么如果打开了很多个Relation,如何管理呢,这就是RealtionIdCache要干的事。
注意这里的400是默认的hash表容量,即最大打开的relation个数。



接下来就是通过 InitCatalogCache();初始化syscache 。
void InitCatalogCache(void){
    int            cacheId;
    Assert(!CacheInitialized);
    MemSet(SysCache, 0, sizeof(SysCache));
    for (cacheId = 0; cacheId < SysCacheSize; cacheId++)    {
        SysCache[cacheId] = InitCatCache(cacheId,
                                         cacheinfo[cacheId].reloid,
                                         cacheinfo[cacheId].indoid,
                                         cacheinfo[cacheId].reloidattr,
                                         cacheinfo[cacheId].nkeys,
                                         cacheinfo[cacheId].key,
                                         cacheinfo[cacheId].nbuckets);
        if (!PointerIsValid(SysCache[cacheId]))
            elog(ERROR, "could not initialize cache %u (%d)",
                 cacheinfo[cacheId].reloid, cacheId);
    }
    CacheInitialized = true;
}
从上面代码知道,用cacheinfo硬编码在源代码中的数据初始化static CatCache *SysCache[lengthof(cacheinfo)]; 这里lengthof(cacheinfo)] = 54;
下图更加清晰一些,注意图中故意把两个将要用到的CatCache 里面的数据列了一下:
这两个CatCache 分别是
static CatCache *SysCache[19]  cc_reloid=1262
我们用mydb=# select oid,relname from pg_class where oid = 1262;一查
 oid  |   relname  
------+-------------
 1262 | pg_database
(1 row)
便知道 表明该项是关于pg_database 这个Relation的。
static CatCache *SysCache[37]  cc_reloid=1259
我们用mydb=# select oid,relname from pg_class where oid=1259 ;一查
 oid  | relname 
------+----------
 1259 | pg_class
(1 row)
便知道 表明该项是关于pg_class 这个Relation的。



在debug窗口看到的CatCache[37]为:
"SysCache[37]" = 0x09bf5e94   
    id = 37   
    cc_next = 0x09bf1d64   
    cc_relname = 0x08488751    "(not known yet)"   
        *cc_relname = '('
    cc_reloid = 1259   
    cc_indexoid = 2663   
    cc_relisshared = false   
    cc_tupdesc = 0x00000000   
    cc_reloidattr = -2   
    cc_ntup = 0   
    cc_nbuckets = 1024   
    cc_nkeys = 2   
    cc_key = 0x09bf5ec0   
        cc_key[0] = 1   
        cc_key[1] = 2   
        cc_key[2] = 0   
        cc_key[3] = 0   
    cc_hashfunc = 0x09bf5ed0   
        cc_hashfunc[0] = 0x00000000   
        cc_hashfunc[1] = 0x00000000   
        cc_hashfunc[2] = 0x00000000   
        cc_hashfunc[3] = 0x00000000   
    cc_skey = 0x09bf5ee0   
        cc_skey[0] = {...}   
            sk_flags = 0   
            sk_attno = 0   
            sk_strategy = 0   
            sk_subtype = 0   
            sk_func = {...}   
                fn_addr = 0x00000000   
                fn_oid = 0   
                fn_nargs = 0   
                fn_strict = false   
                fn_retset = false   
                fn_stats = 0   
                fn_extra = 0x00000000   
                fn_mcxt = 0x00000000   
                fn_expr = 0x00000000   
            sk_argument = 0   
        cc_skey[1] = {...}   
            sk_flags = 0   
            sk_attno = 0   
            sk_strategy = 0   
            sk_subtype = 0   
            sk_func = {...}   
                fn_addr = 0x00000000   
                fn_oid = 0   
                fn_nargs = 0   
                fn_strict = false   
                fn_retset = false   
                fn_stats = 0   
                fn_extra = 0x00000000   
                fn_mcxt = 0x00000000   
                fn_expr = 0x00000000   
            sk_argument = 0   
        cc_skey[2] = {...}   
            sk_flags = 0   
            sk_attno = 0   
            sk_strategy = 0   
            sk_subtype = 0   
            sk_func = {...}   
                fn_addr = 0x00000000   
                fn_oid = 0   
                fn_nargs = 0   
                fn_strict = false   
                fn_retset = false   
                fn_stats = 0   
                fn_extra = 0x00000000   
                fn_mcxt = 0x00000000   
                fn_expr = 0x00000000   
            sk_argument = 0   
        cc_skey[3] = {...}   
            sk_flags = 0   
            sk_attno = 0   
            sk_strategy = 0   
            sk_subtype = 0   
            sk_func = {...}   
                fn_addr = 0x00000000   
                fn_oid = 0   
                fn_nargs = 0   
                fn_strict = false   
                fn_retset = false   
                fn_stats = 0   
                fn_extra = 0x00000000   
                fn_mcxt = 0x00000000   
                fn_expr = 0x00000000   
            sk_argument = 0   
    cc_isname = 0x09bf5f90   
        cc_isname[0] = false   
        cc_isname[1] = false   
        cc_isname[2] = false   
        cc_isname[3] = false   
    cc_lists = {...}   
        dll_head = 0x00000000   
        dll_tail = 0x00000000   
    cc_bucket = 0x09bf5f9c   
        cc_bucket[0] = {...}   
            dll_head = 0x00000000   
            dll_tail = 0x00000000   
和我们图中示意完全一样。
下面是全部的54个系统CatCache信息,我用设置CACHEDEBUG然后 log如下:
DEBUG:  InitCatCache: rel=2600 ind=2650 id=0 nkeys=1 size=32
DEBUG:  InitCatCache: rel=2601 ind=2651 id=1 nkeys=1 size=4
DEBUG:  InitCatCache: rel=2601 ind=2652 id=2 nkeys=1 size=4
DEBUG:  InitCatCache: rel=2602 ind=2654 id=3 nkeys=2 size=64
DEBUG:  InitCatCache: rel=2602 ind=2653 id=4 nkeys=4 size=64
DEBUG:  InitCatCache: rel=2603 ind=2655 id=5 nkeys=4 size=64
DEBUG:  InitCatCache: rel=1249 ind=2658 id=6 nkeys=2 size=2048
DEBUG:  InitCatCache: rel=1249 ind=2659 id=7 nkeys=2 size=2048
DEBUG:  InitCatCache: rel=1261 ind=2695 id=8 nkeys=2 size=128
DEBUG:  InitCatCache: rel=1261 ind=2694 id=9 nkeys=2 size=128
DEBUG:  InitCatCache: rel=1260 ind=2676 id=10 nkeys=1 size=128
DEBUG:  InitCatCache: rel=1260 ind=2677 id=11 nkeys=1 size=128
DEBUG:  InitCatCache: rel=2605 ind=2661 id=12 nkeys=2 size=256
DEBUG:  InitCatCache: rel=2616 ind=2686 id=13 nkeys=3 size=64
DEBUG:  InitCatCache: rel=2616 ind=2687 id=14 nkeys=1 size=64
DEBUG:  InitCatCache: rel=2607 ind=2668 id=15 nkeys=4 size=128
DEBUG:  InitCatCache: rel=2607 ind=2669 id=16 nkeys=2 size=128
DEBUG:  InitCatCache: rel=2606 ind=2667 id=17 nkeys=1 size=1024
DEBUG:  InitCatCache: rel=2607 ind=2670 id=18 nkeys=1 size=128
DEBUG:  InitCatCache: rel=1262 ind=2672 id=19 nkeys=1 size=4
DEBUG:  InitCatCache: rel=3501 ind=3502 id=20 nkeys=1 size=256
DEBUG:  InitCatCache: rel=3501 ind=3503 id=21 nkeys=2 size=256
DEBUG:  InitCatCache: rel=2328 ind=548 id=22 nkeys=1 size=8
DEBUG:  InitCatCache: rel=2328 ind=112 id=23 nkeys=1 size=8
DEBUG:  InitCatCache: rel=1417 ind=549 id=24 nkeys=1 size=32
DEBUG:  InitCatCache: rel=1417 ind=113 id=25 nkeys=1 size=32
DEBUG:  InitCatCache: rel=2610 ind=2679 id=26 nkeys=1 size=1024
DEBUG:  InitCatCache: rel=2612 ind=2681 id=27 nkeys=1 size=4
DEBUG:  InitCatCache: rel=2612 ind=2682 id=28 nkeys=1 size=4
DEBUG:  InitCatCache: rel=2615 ind=2684 id=29 nkeys=1 size=256
DEBUG:  InitCatCache: rel=2615 ind=2685 id=30 nkeys=1 size=256
DEBUG:  InitCatCache: rel=2617 ind=2689 id=31 nkeys=4 size=1024
DEBUG:  InitCatCache: rel=2617 ind=2688 id=32 nkeys=1 size=1024
DEBUG:  InitCatCache: rel=2753 ind=2754 id=33 nkeys=3 size=64
DEBUG:  InitCatCache: rel=2753 ind=2755 id=34 nkeys=1 size=64
DEBUG:  InitCatCache: rel=1255 ind=2691 id=35 nkeys=3 size=2048
DEBUG:  InitCatCache: rel=1255 ind=2690 id=36 nkeys=1 size=2048
DEBUG:  InitCatCache: rel=1259 ind=2663 id=37 nkeys=2 size=1024
DEBUG:  InitCatCache: rel=1259 ind=2662 id=38 nkeys=1 size=1024
DEBUG:  InitCatCache: rel=2618 ind=2693 id=39 nkeys=2 size=1024
DEBUG:  InitCatCache: rel=2619 ind=2696 id=40 nkeys=2 size=1024
DEBUG:  InitCatCache: rel=3603 ind=3609 id=41 nkeys=3 size=4
DEBUG:  InitCatCache: rel=3602 ind=3608 id=42 nkeys=2 size=16
DEBUG:  InitCatCache: rel=3602 ind=3712 id=43 nkeys=1 size=16
DEBUG:  InitCatCache: rel=3600 ind=3604 id=44 nkeys=2 size=16
DEBUG:  InitCatCache: rel=3600 ind=3605 id=45 nkeys=1 size=16
DEBUG:  InitCatCache: rel=3601 ind=3606 id=46 nkeys=2 size=4
DEBUG:  InitCatCache: rel=3601 ind=3607 id=47 nkeys=1 size=4
DEBUG:  InitCatCache: rel=3764 ind=3766 id=48 nkeys=2 size=16
DEBUG:  InitCatCache: rel=3764 ind=3767 id=49 nkeys=1 size=16
DEBUG:  InitCatCache: rel=1247 ind=2704 id=50 nkeys=2 size=1024
DEBUG:  InitCatCache: rel=1247 ind=2703 id=51 nkeys=1 size=1024
DEBUG:  InitCatCache: rel=1418 ind=174 id=52 nkeys=1 size=128
DEBUG:  InitCatCache: rel=1418 ind=175 id=53 nkeys=2 size=128

接下来我们分析分析 InitPlanCache();
/*
 * InitPlanCache: initialize module during InitPostgres.
 *
 * All we need to do is hook into inval.c's callback lists.
 */
void InitPlanCache(void){
    CacheRegisterRelcacheCallback(PlanCacheRelCallback, (Datum) 0);
    CacheRegisterSyscacheCallback(PROCOID, PlanCacheFuncCallback, (Datum) 0);
    CacheRegisterSyscacheCallback(NAMESPACEOID, PlanCacheSysCallback, (Datum) 0);
    CacheRegisterSyscacheCallback(OPEROID, PlanCacheSysCallback, (Datum) 0);
    CacheRegisterSyscacheCallback(AMOPOPID, PlanCacheSysCallback, (Datum) 0);
}
正如源码注释中所说的那样,该函数只是注册了5个callback 函数。

接下来是 EnablePortalManager();
/*
 * EnablePortalManager
 *        Enables the portal management module at backend startup.
 */
void EnablePortalManager(void){
    HASHCTL        ctl;
    Assert(PortalMemory == NULL);
    PortalMemory = AllocSetContextCreate(TopMemoryContext,
                                         "PortalMemory",
                                         ALLOCSET_DEFAULT_MINSIZE,
                                         ALLOCSET_DEFAULT_INITSIZE,
                                         ALLOCSET_DEFAULT_MAXSIZE);
    ctl.keysize = MAX_PORTALNAME_LEN;
    ctl.entrysize = sizeof(PortalHashEnt);

    /*
     * use PORTALS_PER_USER as a guess of how many hash table entries to
     * create, initially
     */
    PortalHashTable = hash_create("Portal hash", PORTALS_PER_USER,&ctl, HASH_ELEM);
}
我们发现该函数仅仅是做了又创建了一个hash表,so simple的事情。该hash表早就是我们的老东家了,此处不再上图。注意PORTALS_PER_USER =16。

接下来是一直让我们做噩梦的 RelationCacheInitializePhase2();现在我们要看看他老人家有多恐怖。我们先列列有哪些函数调用:
  • load_relcache_init_file()
  • formrdesc("pg_class", PG_CLASS_RELTYPE_OID,
                      true, Natts_pg_class, Desc_pg_class);
            formrdesc("pg_attribute", PG_ATTRIBUTE_RELTYPE_OID,
                      false, Natts_pg_attribute, Desc_pg_attribute);
            formrdesc("pg_proc", PG_PROC_RELTYPE_OID,
                      true, Natts_pg_proc, Desc_pg_proc);
            formrdesc("pg_type", PG_TYPE_RELTYPE_OID,
                      true, Natts_pg_type, Desc_pg_type);
  • load_critical_index(ClassOidIndexId,
                                RelationRelationId);
            load_critical_index(AttributeRelidNumIndexId,
                                AttributeRelationId);
            load_critical_index(IndexRelidIndexId,
                                IndexRelationId);
            load_critical_index(OpclassOidIndexId,
                                OperatorClassRelationId);
            load_critical_index(AccessMethodStrategyIndexId,
                                AccessMethodOperatorRelationId);
            load_critical_index(AccessMethodProcedureIndexId,
                                AccessMethodProcedureRelationId);
            load_critical_index(OperatorOidIndexId,
                                OperatorRelationId);
            load_critical_index(RewriteRelRulenameIndexId,
                                RewriteRelationId);
            load_critical_index(TriggerRelidNameIndexId,
                                TriggerRelationId);
  • hash_seq_init(&status, RelationIdCache);
  • while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL){}
架势不小呀,我们一个一个来攻克。
九阴真经之load_relcache_init_file()
先看看注释:
 *           +  When the backend is started up in normal mode, we load an image
 *              of the appropriate relation descriptors, in internal format,
 *              from an initialization file in the data/base/... directory.
 *
 *           +  If the initialization file isn't there, then we create the
 *              relation descriptors using sequential scans and write 'em to
 *              the initialization file for use by subsequent backends.
首先该文件为char    initfilename ="base/16384/pg_internal.init"。上面的注释说明,如果不存在该文件,则顺序扫描并创建relation descriptors,之后写入该文件。如果该文件存在,则简单的load进内存,填充 relation descriptors。
A.先假定第一次,我们没有base/16384/pg_internal.init 该文件。则该 load_relcache_init_file()仅仅是返回false。不做任何工作,进而进入
        formrdesc("pg_class", PG_CLASS_RELTYPE_OID,
                  true, Natts_pg_class, Desc_pg_class);
        formrdesc("pg_attribute", PG_ATTRIBUTE_RELTYPE_OID,
                  false, Natts_pg_attribute, Desc_pg_attribute);
        formrdesc("pg_proc", PG_PROC_RELTYPE_OID,
                  true, Natts_pg_proc, Desc_pg_proc);
        formrdesc("pg_type", PG_TYPE_RELTYPE_OID,
                  true, Natts_pg_type, Desc_pg_type);
阶段。并接下来进入:
load_critical_index(ClassOidIndexId,
                            RelationRelationId);
        load_critical_index(AttributeRelidNumIndexId,
                            AttributeRelationId);
        load_critical_index(IndexRelidIndexId,
                            IndexRelationId);
        load_critical_index(OpclassOidIndexId,
                            OperatorClassRelationId);
        load_critical_index(AccessMethodStrategyIndexId,
                            AccessMethodOperatorRelationId);
        load_critical_index(AccessMethodProcedureIndexId,
                            AccessMethodProcedureRelationId);
        load_critical_index(OperatorOidIndexId,
                            OperatorRelationId);
        load_critical_index(RewriteRelRulenameIndexId,
                            RewriteRelationId);
        load_critical_index(TriggerRelidNameIndexId,
                            TriggerRelationId);
阶段。然后是
hash_seq_init(&status, RelationIdCache);
while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
阶段。
最后是如果没有"base/16384/pg_internal.init"文件,则写入到 base/16384/pg_internal.init 文件中去。
我们先来看看第一步:
formrdesc("pg_class", PG_CLASS_RELTYPE_OID,
                  true, Natts_pg_class, Desc_pg_class);
我们先看看这个函数的原型:
static void formrdesc
(const char *relationName, Oid relationReltype,bool hasoids, int natts, FormData_pg_attribute *att){}
那么传入的参数为:
formrdesc("pg_class", 83, true, 25, Desc_pg_class);
那么Desc_pg_class是什么呢,我们来看看定义:
static FormData_pg_attribute Desc_pg_class[Natts_pg_class] = {Schema_pg_class};
而Schema_pg_class又定义为:
/* ----------------
 *        pg_class
 * ----------------
 */
#define Schema_pg_class /
{ 1259, {"relname"},       19, -1, NAMEDATALEN, 1, 0, -1, -1, false, 'p', 'c', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relnamespace"},  26, -1,    4,    2, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, /
{ 1259, {"reltype"},       26, -1,    4,    3, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relowner"},       26, -1,    4,    4, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relam"},           26, -1,    4,    5, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relfilenode"},   26, -1,    4,    6, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, /
{ 1259, {"reltablespace"}, 26, -1,    4,    7, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relpages"},       23, -1,    4,    8, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, /
{ 1259, {"reltuples"},       700, -1, 4,    9, 0, -1, -1, FLOAT4PASSBYVAL, 'p', 'i', true, false, false, true, 0, { 0 } }, /
{ 1259, {"reltoastrelid"}, 26, -1,    4, 10, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, /
{ 1259, {"reltoastidxid"}, 26, -1,    4, 11, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relhasindex"},   16, -1,    1, 12, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relisshared"},   16, -1,    1, 13, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relistemp"},       16, -1,    1, 14, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relkind"},       18, -1,    1, 15, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relnatts"},       21, -1,    2, 16, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relchecks"},       21, -1,    2, 17, 0, -1, -1, true, 'p', 's', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relhasoids"},    16, -1,    1, 18, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relhaspkey"},    16, -1,    1, 19, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relhasrules"},   16, -1,    1, 20, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relhastriggers"},16, -1,    1, 21, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relhassubclass"},16, -1,    1, 22, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relfrozenxid"},  28, -1,    4, 23, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0, { 0 } }, /
{ 1259, {"relacl"},         1034, -1, -1, 24, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }, /
{ 1259, {"reloptions"},  1009, -1, -1, 25, 1, -1, -1, false, 'x', 'i', false, false, false, true, 0, { 0 } }
可见Desc_pg_class主要是定义了pg_class表结构里的25个字段相关的信息。
那么回头我们看看formrdesc()函数里主要干了什么事情,经过一分析,我们知道
Relation    relation;
relation = (Relation) palloc0(sizeof(RelationData));
......./*填充数据*/
RelationCacheInsert(relation); /*在上面刚刚创建的RelationIdCache哈希表中登记一下*/
这样子我们可以画个图,看看这个Relation到底是什么。图中只画出RelationIdCache里的hash表项,hash值我直接省略。

那么上面的图是否正确呢?我们debug一下,看看这个relation到底什么样。
relation    0x09812f90   
    rd_node    {...}   
        spcNode    1663   
        dbNode    16384   
        relNode    1259   
    rd_smgr    0x00000000   
    rd_targblock    4294967295   
    rd_refcnt    1   
    rd_istemp    false   
    rd_islocaltemp    false   
    rd_isnailed    true   
    rd_isvalid    false   
    rd_indexvalid    0   
    rd_createSubid    0   
    rd_newRelfilenodeSubid    0   
    rd_rel    0x0981b040   
        relname    {...}   
            data    0x0981b040   
        relnamespace    11   
        reltype    83   
        relowner    0   
        relam    0   
        relfilenode    1259   
        reltablespace    0   
        relpages    1   
        reltuples    1.0   
        reltoastrelid    0   
        reltoastidxid    0   
        relhasindex    true   
        relisshared    false   
        relistemp    false   
        relkind    'r'   
        relnatts    25   
        relchecks    0   
        relhasoids    true   
        relhaspkey    false   
        relhasrules    false   
        relhastriggers    false   
        relhassubclass    false   
        relfrozenxid    0   
        relacl    0x0981b0bc   
            relacl[0]    126   
        reloptions    0x0981b0c0   
    rd_att    0x09838780   
        natts    25   
        attrs    0x0983879c   
            attrs[0]    0x09838800   
                attrelid    1259   
                attname    {...}   
                    data    0x09838804   
                        data[0]    'r'   
                        data[1]    'e'   
                        data[2]    'l'   
                        data[3]    'n'   
                        data[4]    'a'   
                        data[5]    'm'   
                        data[6]    'e'   
                        data[7]    0   
                        data[8]    0   
                        data[9]    0   
                         ...
                        data[63]       
                atttypid    19   
                attstattarget    -1   
                attlen    64   
                attnum    1   
                attndims    0   
                attcacheoff    0   
                atttypmod    -1   
                attbyval    false   
                attstorage    'p'   
                attalign    'c'   
                attnotnull    true   
                atthasdef    false   
                attisdropped    false   
                attislocal    true   
                attinhcount    0   
                attacl    0x09838868   
            attrs[1]    0x09838868   
            attrs[2]    0x098388d0   
            attrs[3]    0x09838938   
            attrs[4]    0x098389a0   
            ....
            attrs[23]    0x09839158   
            attrs[24]    0x098391c0   
        constr    0x09806e88   
        tdtypeid    83   
        tdtypmod    -1   
        tdhasoid    true   
        tdrefcount    1   
    rd_id    1259   
    rd_indexlist    0x00000000   
    rd_indexattr    0x00000000   
    rd_oidindex    0   
    rd_lockInfo    {...}   
        lockRelId    {...}   
            relId    1259   
            dbId    16384   
    rd_rules    0x00000000   
    rd_rulescxt    0x00000000   
    trigdesc    0x00000000   
    rd_options    0x00000000   
    rd_index    0x00000000   
    rd_indextuple    0x00000000   
    rd_am    0x00000000   
    rd_indexcxt    0x00000000   
    rd_aminfo    0x00000000   
    rd_opfamily    0x00000000   
    rd_opcintype    0x00000000   
    rd_operator    0x00000000   
    rd_support    0x00000000   
    rd_supportinfo    0x00000000   
    rd_indoption    0x00000000   
    rd_indexprs    0x00000000   
    rd_indpred    0x00000000   
    rd_amcache    0x00000000   
    rd_fsm_nblocks    4294967295   
    rd_vm_nblocks    4294967295   
    pgstat_info    0x00000000   
可见完全正确。
剩余的其他三项: formrdesc("pg_attribute", PG_ATTRIBUTE_RELTYPE_OID,
                  false, Natts_pg_attribute, Desc_pg_attribute);
        formrdesc("pg_proc", PG_PROC_RELTYPE_OID,
                  true, Natts_pg_proc, Desc_pg_proc);
        formrdesc("pg_type", PG_TYPE_RELTYPE_OID,
                  true, Natts_pg_type, Desc_pg_type);
分析就类似了,不再详述。
我们先来看看第二步:
load_critical_index(ClassOidIndexId,
                            RelationRelationId);
看看这里面都初始化了哪些内容。
上式实际上是调用static void load_critical_index(2662, 1259)。在该函数中实际上只调用了:
Relation    ird;
ird = RelationBuildDesc(2662, true);
我们看看RelationBuildDesc()有什么?
pg_class_tuple = ScanPgRelation(targetRelId, true);   其中 targetRelId=2662
relid = HeapTupleGetOid(pg_class_tuple);
relp = (Form_pg_class) GETSTRUCT(pg_class_tuple);
relation = AllocateRelationDesc(relp);
...
RelationBuildTupleDesc(relation);
....
我们发现纵观全文从开始到现在,唯独这个 ScanPgRelation(targetRelId, true)最复杂,具体细节我不在展开,这里我只是说明该函数的作用就是首先把该pg_class 的6个page中的第一个读入到内存,顺序抽取各个tuple,比较oid是否等于2662,若相等,则返回,若不相等,则读入第2个page,用于描述该过程的数据结构是MdfdVec->mdfd_chain。
(Form_pg_class) GETSTRUCT(pg_class_tuple);作用就是把该tupe实际指的数据抽取填充新建的relation的rd_rel指向的Form_pg_class数据结构。
RelationBuildTupleDesc(relation);主要是填充一些默认数值。这样我们得出如下几张图,图中绘出了用到的数据结构,以及新生成的oid为2662 index的relation。
下图是在这个阶段中对pg_class的relation数据结构的修改:

下图是这阶段搜索tuple用到的数据结构:


下图是新生成的oid为2662的新的relation数据结构:

剩余的load index 类似,这里不再展开描述。
接下来我们进入 第三个阶段:
hash_seq_init(&status, RelationIdCache); 很简单,只是初始化了几个变量
while ((idhentry = (RelIdCacheEnt *) hash_seq_search(&status)) != NULL)
{}
所以我们来看看while循环。
其实这里面非常简单,就是顺序扫描 RelationIdCache hash表,找到每一项 RelIdCacheEnt ,继而找到该relation,如果该relation->rd_rel->relhasrules 和relhastriggers为true,则导入相应的rules和triggers,我们这里就暂时不分析如何导入这些rules和triggers了。
接下来我们进入 第四个阶段:把相应的 RelationIdCache 内容写到 base/16384/pg_internal.init 文件中去。
由于涉及的内容不复杂,这一步我们不分析了。

B.如果我们已经有了base/16384/pg_internal.init 该文件,则 load_relcache_init_file() 的代码用于导入到该映像文件到内存中去了。
由于内容对我们理解后续的步骤不是很重要,暂缓分析。
至此,我们的对 RelationCacheInitializePhase2()的分析告一段落,这里做个小结,这个阶段主要是在 RelationIdCache 中生成几个系统表和index在内存中的relation表示。由此可见relation是在内存中的核心表示。


接下来是 CheckMyDatabase(dbname, am_superuser);
这个阶段做的工作主要是扫描SysCache[54]中的DATABASEOID项,如果TupleDesc    cc_tupdesc=NULL,则创建该relation并把该relation的TupleDesc    rd_att属性赋给cc_tupdesc,并打开pg_database relation,顺序读取其中oid为 dbname的中的tuple,并把该tuple的encoding等信息,赋给DatabaseEncoding等全局变量。
所以该阶段做了两个事情,一个是创建oid=DATABASEOID的relation内存数据结构,然后顺序读取该relation中的各个tuple,找到oid= dbname的tuple,读取各个参数,赋给全局变量以及更新SysCache中的相应项。

接下来是 InitializeSearchPath();
注册了一个回调函数,并设置
baseSearchPathValid = false;
接下来是 InitializeClientEncoding();
实际上很简单,就是设置static pg_enc2name *clientEncoding =PG_UTF8


至此,全部结束。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值