设为首页 收藏本站
查看: 750|回复: 0

[经验分享] PostgreSQL启动过程中的那些事十六:启动进程三:CheckPointGuts刷出共享内存里所有数据

[复制链接]

尚未签到

发表于 2016-11-21 08:01:44 | 显示全部楼层 |阅读模式
       话说启动进程调用StartupXLOG启动xlog,根据情况,如果需要就排除系统故障引起的数据库不一致状态,做相应的REDO或UNDO,然后创建一个检查点,把所有共享内存磁盘缓冲和提交数据缓冲写并文件同步到磁盘、把检查点插入xlog文件、更新控制文件,使数据库达到一种状态。
这节接着讨论启动进程在创建检查点时调用的CheckPointGuts方法(在创建重启点时也会调用这个方法)。CheckPointGuts方法功能是刷出所有共享内存中的数据到磁盘并做文件同步,共享内存中的数据包括clogsubtransmultixactpredicaterelationmapbuffer(数据文件)和twophase相关数据。CheckPointGuts方法定义和“CheckPointGuts方法调用序列图”见下面。
 
static void

CheckPointGuts(XLogRecPtrcheckPointRedo, int flags)

{

    CheckPointCLOG();

    CheckPointSUBTRANS();

    CheckPointMultiXact();

    CheckPointPredicate();

    CheckPointRelationMap();

    CheckPointBuffers(flags);   /* performs all required fsyncs */

    /* We deliberately delay 2PC checkpointingas long as possible */

    CheckPointTwoPhase(checkPointRedo);

}
 
DSC0000.png

CheckPointGuts方法调用序列图

  
 
  
CheckPointGuts方法主要是通过调用提交事务日志管理器的方法CheckPointClog,子事务日志管理器的方法CheckPointSUBTRANS,多事务日志管理器的方法CheckPointMultiXact,支持序列化事务隔离级别的谓词锁模块的方法CheckPointPredicate,目录/系统表到文件节点映射模块的方法CheckPointRelationMap,缓存管理器的方法CheckPointBuffers,两阶段提交模块的方法CheckPointTwoPhase把共享内存里的数据刷出并文件同步到磁盘。
  
其中提交事务日志管理器的方法CheckPointClog、子事务日志管理器的方法CheckPointSUBTRANS、多事务日志管理器的方法CheckPointMultiXact、多事务日志管理器的方法CheckPointMultiXact、支持序列化事务隔离级别的谓词锁模块的方法CheckPointPredicate最后都调用了SLRU模块的SimpleLruFlush方法,把相关共享内存数据写到磁盘,并调用pg_fsync方法把相关内容文件同步到磁盘上对应文件。
  
在缓存管理器的方法CheckPointBuffers,两阶段提交模块的方法CheckPointTwoPhase里,因为没有使用SLRU算法,直接调用pg_fsync方法把相关内容文件同步到磁盘上对应文件。
  
在目录/系统表到文件节点映射模块的方法CheckPointRelationMap里,在释放RelationMappingLock时,会完成共享内存里相关系统表和对应物理文件映射的文件同步到磁盘工作。
  
我们看一下各种日志管理,日志对数据库是至关重要的一部分,出现系统故障时,数据库通过重放日志恢复数据,保证数据库一致性和完整性。
  
Pg里有XLOGCLOGSUBTRANS LOGMultiXactID LOG四种事务日志,XLOG是事务日志,就是平时常说的REDOLOG,记录了事务操作数据库的过程信息和事务最终状态;CLOGXLOG里事务的提交状态日志;SUBTRANS是子事务日志,为每一个事务存储父事务ID。这是嵌套事务实现的基础部分,SUBTRANS仅需要为当前打开的事务记住信息,没有必要在崩溃并重启后保留数据;MultiXactID是组合事务日志,由一组事务ID组成,是共享行锁shared-row-lock实现的基础部分,共享锁锁住的元组在其Xmax字段存储MultiXactId。各种日志都存放在对应的日志文件里。
  
有了文件就有了I/O,为了降低I/O开销,pg设置了各种日志的缓存区,由对应的日志管理器管理日志的写、文件同步和读等日志维护工作。Pg使用简单最近最少使用(SLRU)算法来管理事务日志。使用轻量锁LWLock的ControlLock锁保护整个缓冲区,其中的每个缓冲块(默认8K)还有一个LWLock锁保护,以控制并发操作。SLRU及事务日志的部分相关数据结构在下面。
  
为CLOG控制链接到共享内存数据结构

  
static SlruCtlData ClogCtlData;

  
#define ClogCtl (&ClogCtlData)

  
 

  
为SUBTRANS控制链接到共享内存数据结构

  
static SlruCtlData SubTransCtlData;

  
#define SubTransCtl (&SubTransCtlData)

  
 

  
为MultiXact控制链接到共享内存数据结构

  
  
static SlruCtlData MultiXactOffsetCtlData;

  
static SlruCtlData MultiXactMemberCtlData;

  
#define MultiXactOffsetCtl  (&MultiXactOffsetCtlData)

  
#define MultiXactMemberCtl  (&MultiXactMemberCtlData)

  
 

  
 

  
typedef SlruCtlData *SlruCtl;
  
 
  
/* SlruCtlData是指向共享内存里的活跃信息的非共享结构*/

  
typedef struct SlruCtlData

  
{

  
    SlruShared shared;

  
 

  
    /*这个标志告诉 是否文件同步写(pg_clogmultixact成员是true,pg_subtranspg_notifyfalse */

  
    bool       do_fsync;

  
 

  
    /*为截断目的决定两个页号哪一个是更旧的。为了用 包裹 XID算法(with wraparoundXID arithmetic)做正确的事,这儿我们需要用事务ID比较 */

  
    bool       (*PagePrecedes) (int, int);

  
 

  
    /* SimpleLruInit期间目录被设置,并且从那以后不变。因为它总是相同的,它不必放到共享内存里。 */

  
    char       Dir[64];

  
} SlruCtlData;

  
 
  
共享内存状态

  
typedef struct SlruSharedData

  
{

  
    LWLockId   ControlLock;

  
 

  
    /* 由这个SLRU结构管理的缓存块号*/

  
    int        num_slots;

  
 

  
    /*持有每一个缓存槽信息的数组。当状态是EMPTY 缓存页/块号是未定义的,当作

  
page_lru_count */

  
    char     **page_buffer;

  
    SlruPageStatus *page_status;

  
    bool     *page_dirty;

  
    int       *page_number;

  
    int       *page_lru_count;

  
    LWLockId   *buffer_locks;

  
 

  
    /* SLRU/块里的相关条目的WAL刷出LSN的可选数组。如果不是0/NULL,在写缓存页/块前 我们必须刷出WALpg_clogtruemultixactpg_subtranspg_notifyfalse)。Group_lsn[]每缓存页/块槽有lsn_groups_per_page条目,在这个槽的缓存页/块上 SLRU条目的一个临近组 每一个缓存页/块槽 包含最高已知LSN */

  
    XLogRecPtr *group_lsn;

  
    int        lsn_groups_per_page;

  
 

  
    /*我们通过设置page_lru_count[slotno] = ++cur_lru_count标记页“最近使用”;最老旧页因此是有表达式cur_lru_count - page_lru_count[slotno]值最高/大的那一个。这个数事实上包裹,但这个计算仍然工作 和缓存页/块的年龄(超过了INT_MAX数)一样长。   */

  
    int        cur_lru_count;

  
 

  
    /* latest_page_number是当前日志结尾的页/块号;这不是严格的数据,因为我们仅用它避免包裹swapping出了最后的页/块。 */

  
    int        latest_page_number;

  
} SlruSharedData;

  
 

  
typedef SlruSharedData *SlruShared;

  
 

  
/*页状态代码。注意这不包含"dirty"位。仅在VALID或者WRIT_IN_PROGRESS状态里page_dirty能是true;在后面的例子/情况里 它暗示 从这次写开始后页又被搞脏*/

  
typedef enum

  
{

  
    SLRU_PAGE_EMPTY,         /* buffer is not in use */

  
    SLRU_PAGE_READ_IN_PROGRESS, /* page is beingread in */

  
    SLRU_PAGE_VALID,         /* page is valid and not being written */

  
    SLRU_PAGE_WRITE_IN_PROGRESS /* page is beingwritten out */

  
} SlruPageStatus;

  
 
  
 
  
SLRU算法的缓存区操作在下面,其中包括了本节多次调用的SimpleLruFlush方法,将缓存数据刷出并文件同步到磁盘。
  
extern Size SimpleLruShmemSize(int nslots, int nlsns);

  
extern void SimpleLruInit(SlruCtl ctl, const char *name, int nslots, int nlsns,

  
             LWLockId ctllock, const char *subdir);

  
extern int SimpleLruZeroPage(SlruCtl ctl, int pageno);

  
extern int SimpleLruReadPage(SlruCtl ctl, int pageno, bool write_ok,

  
                TransactionId xid);

  
extern int SimpleLruReadPage_ReadOnly(SlruCtl ctl, int pageno,

  
                        TransactionId xid);

  
extern void SimpleLruWritePage(SlruCtl ctl, int slotno);

  
extern void SimpleLruFlush(SlruCtl ctl, bool checkpoint);

  
extern void SimpleLruTruncate(SlruCtl ctl, int cutoffPage);

  
extern bool SlruScanDirectory(SlruCtl ctl, int cutoffPage, bool doDeletions);

  
 
就到这儿吧。
 


------------
转载请著明出处,来自博客:
blog.csdn.net/beiigang
beigang.iyunv.com

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-303140-1-1.html 上篇帖子: PostgreSQL启动过程中的那些事七:初始化共享内存和信号十六:shmem中初始化BgWriter 下篇帖子: PostgreSQL服务过程中的那些事二:Pg服务进程处理简单查询二:SQL解析为parsetree
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表