PostgreSQL启动过程中的那些事七:初始化共享内存和信号:四 shmem中初始化subtrans

       pg 初始化 shmem ,给其加上索引 "ShmemIndex" 后,接着就在 shmem 里初始化 xlog 。然后依次初始化 clog subtrans twophase multixact 。安排按 clog subtrans multixact twophase 的顺序写,把 twophase 放到 multixact 之后是因为前面三个用了相同的算法和数据结构,连起来写可以加深印象和归类记忆,本来想把初始化 clog subtrans multixact 放到一篇文章里写,因为篇幅太长还是分开了,看的时候这几篇文章可以结合起来看。

      

pg 子事务管理器( pg_subtrans manager )是一个类提交事务管理器( pg_clog-like manager ,关于 clog 见《 pg 启动过程中的那些事七 - 三》。),为每一个事务存储父事务 ID 。它是实现嵌套事务的一个基础部分。主事务有一个无效的父事务 ID ,且每个子事务以其为父事务。这颗事务树能方便的从子事务找到父事务,但不能从父事务找到子事务。子事务只需要为当前打开的事务记住子事务信息。这样没有必要像 CLOG 那样为进程崩溃或重启数据库保存数据。

因为不保存因崩溃恢复需要的数据,所以没有和 XLOG 的交互。在 pg 数据库启动期间,只要把子事务的当前活跃页置 0 就可以了。

上面概述了子事务,下来我们看方法调用流程

 

1 先上个图,看一下函数调用过程梗概,中间略过部分细节

 
PostgreSQL启动过程中的那些事七:初始化共享内存和信号:四 shmem中初始化subtrans_第1张图片

初始化 SUBTRANS 方法调用流程图

 

2 初始化 xlog 相关结构

话说 main()->…->PostmasterMain()->…->reset_shared() -> CreateSharedMemoryAndSemaphores()->…-> CLOGShmemInit () ,初始化子事务相关数据结构 ClogCtlData 等,用作内存里管理和缓存子事务日志文件(存放在 "data/pg_subtrans" 文件夹里的文件)。

SUBTRANSShmemInit () 调用 ShmemInitStruct() 在其中 调用 hash_search() 在哈希表索引 "ShmemIndex" 中查找 "SUBTRANS Ctl " ,如果没有,就在 shmemIndex 中给 " SUBTRANS Ctl " 分一个 HashElement ShmemIndexEnt entry ,在其中的 Entry 中写上 "SUBTRANS Ctl " 。返回 ShmemInitStruct() ,再调用 ShmemAlloc() 在共享内存上给 "SUBTRANS Ctl " 相关结构(见下面“ subtrans 相关结构图” )分配空间,设置 entry (在这儿及ShmemIndexEnt 类型变量)的成员 location 指向该空间, size 成员记录该空间大小 最后返回 SUBTRANSShmemInit () ,让 SlruCtlData * 类型 全局变量 SubTransCtl 指向 SlruCtlData 类型静态 全局变量 SubTransCtlData SubTransCtlData 的起始地址就是在shmem 里给 " SUBTRANS Ctl" 相关结构分配的内存起始地址,设置其中SubTransCtlData 结构类型的成员值。 相关变量、结构定义和 初始化完成后数据结构图在下面。

 

#define SubTransCtl  (&SubTransCtlData)

static SlruCtlData SubTransCtlData;

 

typedef struct SlruCtlData

{

    SlruShared  shared;

 

    /*

      * This flag tells whether to fsync writes (true for pg_clog, false for

      * pg_subtrans).

      */

    bool         do_fsync;

 

    /*

      * Decide which of two page numbers is "older" for truncation purposes. We

      * need to use comparison of TransactionIds here in order to do the right

      * thing with wraparound XID arithmetic.

      */

    bool         (*PagePrecedes) (int , int );

 

    /*

      * Dir is set during SimpleLruInit and does not change thereafter. Since

      * it's always the same, it doesn't need to be in shared memory.

      */

    char         Dir[64];

} SlruCtlData;

 

typedef SlruCtlData *SlruCtl;

 

/*

  * Shared-memory state

  */

typedef struct SlruSharedData

{

    LWLockId    ControlLock;

 

    /* Number of buffers managed by this SLRU structure */

    int          num_slots;

 

    /*

      * Arrays holding info for each buffer slot.  Page number is undefined

      * when status is EMPTY, as is page_lru_count.

      */

    char       **page_buffer;

    SlruPageStatus *page_status;

    bool        *page_dirty;

    int         *page_number;

    int         *page_lru_count;

    LWLockId   *buffer_locks;

    /*

      * Optional array of WAL flush LSNs associated with entries in the SLRU

      * pages.  If not zero/NULL, we must flush WAL before writing pages (true

      * for pg_clog, false for multixact, pg_subtrans, pg_notify).  group_lsn[]

      * has lsn_groups_per_page entries per buffer slot, each containing the

      * highest LSN known for a contiguous group of SLRU entries on that slot's

      * page.

      */

    XLogRecPtr *group_lsn;

    int         lsn_groups_per_page;

    /*----------

      * We mark a page "most recently used" by setting

      *      page_lru_count[slotno] = ++cur_lru_count;

      * The oldest page is therefore the one with the highest value of

      *      cur_lru_count - page_lru_count[slotno]

      * The counts will eventually wrap around, but this calculation still

      * works as long as no page's age exceeds INT_MAX counts.

      *----------

*/

    int          cur_lru_count;

 

    /*

      * latest_page_number is the page number of the current end of the log;

      * this is not critical data, since we use it only to avoid swapping out

      * the latest page.

      */

    int          latest_page_number;

} SlruSharedData;

 

typedef SlruSharedData *SlruShared;

 

下面看看初始化完 " SUBTRANS Ctl" 相关结构后在内存中的结构图


PostgreSQL启动过程中的那些事七:初始化共享内存和信号:四 shmem中初始化subtrans_第2张图片

 

初始化完 SUBTRANS 相关结构 的内存结构图

       为了精简上图,把创建 shmem 的哈希表索引 "ShmemIndex" 时创建的 HCTL 结构删掉了,这个结构的作用是记录创建可扩展哈希表的相关信息。增加了左边灰色底的部分,描述 共享内存 /shmem 里各变量物理布局概览,由下往上,由低地址到高地址。其中的 " SUBTRANS Ctl" clog 的相关结构图下面分别给出,要不上面的图太大太复杂了。

 

PostgreSQL启动过程中的那些事七:初始化共享内存和信号:四 shmem中初始化subtrans_第3张图片

 

SUBTRANS 相关结构图

你可能感兴趣的:(PostgreSQL,共享内存,子事务)