了解更多Greenplum相关内容,欢迎访问Greenplum中文社区网站
《深入浅出Greenplum内核》系列直播已经顺利完结啦!全部十场的视频内容可以前往Greenplum中文社区B站频道观看相关视频,相关PPT均已上传Greenplum中文社区网站(cn.greenplum.org)的下载页面,欢迎获取!现在让我们来回顾一下完结场活动《揭秘Greenplum恢复(Recovery)系统》的精华内容。
揭秘Greenplum恢复(Recovery)系统
视频如和文字部分有出入,请以文字为准
今天我们将给大家详细解析Greenplum恢复系统。恢复系统对于一个数据库系统是必不可少的一部分。本文将围绕4个部分进行讲述,本文内容如非特殊声明,均基于Greenplum 6。
- 恢复系统概述
- 预写式日志简介
- 单节点系统恢复
- 分布式系统恢复
首先我们会对恢复系统相关概念和预写式日志进行介绍,接着会介绍单点式的恢复系统,Greenplum在这一点上与PostgreSQL很接近,但由于Greenplum是个分布式数据库系统,因此在例如两阶段恢复等地方和PostgreSQL会有不同之处。最后我们会介绍分布式系统恢复,Greenplum作为分布式数据库系统,除了单个节点的恢复,也需要保证整个集群的一致性,本文将着重介绍这一部分。
一、系统恢复概述
在现实生活中,数据库系统可能会遇到各种各样的故障。常见的故障大致可以被分为三类:
-
硬件故障。例如异常断电、磁盘访问出现故障,网卡出问题等,此时,就需要针对具体情况进行恢复。
-
系统故障。即软件故障,这里和数据库没有太大关系,原因有多种,有可能是内核崩溃,依赖库bug等情况导致的。这一块也不是我们今天重点要介绍的内容。
-
Postgres进程异常退出。例如OOM,bug,保护性PANIC等。这将是本文着重介绍的故障类型。在遇到这种故障时,如果是除postmaster之外的单点进程退出,整个单点Postgres会进行重启。
PostgreSQL和Greenplum的恢复系统都是基于WAL,即预写式日志。所谓预写式日志是指事务提交先写日志,事务引起的数据修改通常可以滞后,Greenplum的segment上事务提交时日志同步到从节点后才认为成功。一些表数据的页面修改可以稍后完成,从而保证了较好的性能。
在单节点上,恢复系统通常是从Checkpoint日志中redo值所代表的的位置开始重放日志。通常情况下会读取最新的checkpoint中的日志,但在某些情况下会需要读取上次的checkpoint中的日志。
Greenplum作为一个分布式数据库系统,需要分布式事务管理器和分布式恢复,而Master节点充当了这两个角色,保证整个集群的数据全局一致性。在数据恢复系统中某些场景下,需要进行人工干预:例如一些产品bug中,startup进程出现异常等情况,但这种场景较少见。如果出现需要人工干预的场景,商业用户可以寻求原厂的支持。
二、预写式日志简介
首先我们来看一个简单Insert的预写式日志的例子。
postgres=# insert into t1 select generate_series(1,10);
INSERT 0 10
上面的Insert语句中,写入了十个tuple,被分布到各个节点上,这是一个典型的两阶段事务。我们以某一个Primary为例,以完成时间顺序看这个两阶段事务日志写入情况(下为pg_xlogdump解析日志输出结果)。
-
第一个Primary节点(其余 Primary节点类似)
rmgr: Heap len (rec/tot): 31/ 63, tx: 721, lsn: 0/0C0F02A0, prev 0/0C0F0238, bkp: 0000, desc: insert: rel 1663/12812/16391; tid 0/6
rmgr: Heap len (rec/tot): 31/ 63, tx: 721, lsn: 0/0C0F02E0, prev 0/0C0F02A0, bkp: 0000, desc: insert: rel 1663/12812/16391; tid 0/7
rmgr: Heap len (rec/tot): 31/ 63, tx: 721, lsn: 0/0C0F0320, prev 0/0C0F02E0, bkp: 0000, desc: insert: rel 1663/12812/16391; tid 0/8
rmgr: Heap len (rec/tot): 31/ 63, tx: 721, lsn: 0/0C0F0360, prev 0/0C0F0320, bkp: 0000, desc: insert: rel 1663/12812/16391; tid 0/9
rmgr: Heap len (rec/tot): 31/ 63, tx: 721, lsn: 0/0C0F03A0, prev 0/0C0F0360, bkp: 0000, desc: insert: rel 1663/12812/16391; tid 0/10
rmgr: Transaction len (rec/tot): 376/ 408, tx: 721, lsn: 0/0C0F03E0, prev 0/0C0F03A0, bkp: 0000, desc: prepare
-
Master节点
rmgr: Transaction len (rec/tot): 84/ 116, tx: 0, lsn: 0/0C0FC9C8, prev 0/0C0FC988, bkp: 0000, desc: distributed commit 2021-01-12 16:29:12.085955 CST gid = 4129264608-0000032766 gid = 1610432654-0000000012, gxid = 12
-
第一个Primary节点(其余priamry节点类似)
rmgr: Transaction len (rec/tot): 72/ 104, tx: 0, lsn: 0/0C0F0578, prev 0/0C0F03E0, bkp: 0000, desc: commit prepared 721: 2021-01-12 16:29:12.086621 CST gid = 139787424-0000000000 gid = 1610432654-0000000012 gxid = 12
-
Master 节点
rmgr: Transaction len (rec/tot): 28/ 60, tx: 0, lsn: 0/0C0FCA40, prev 0/0C0FC9C8, bkp: 0000, desc: distributed forget gid = 1610432654-0000000012, gxid = 12
两阶段事务的参与者prepare后,协调器会发commit prepare给所有的节点。Master的节点接收到所有节点的prepare信息后,如果其中一个节点prepare不成功,就会给所有的节点发prepare回滚的指令。如果所有的节点prepare成功,Master会在本地写一个distributed commit日志,所有的primary节点才会发起两阶段提交,因此在上面的例子中,我们会看到 Primary节点上会有一个commit prepared的日志。最后master节点上会写一个distributed forget的日志。
虽然整个流程较为简单,但有很多细节需要考量。这里我们可以思考三个问题:
问题1:为什么master先做distributed commit再发起segment上两阶段提交?
问题2