Postgres XL管理与维护

Postgres-XL本身是分布式的多节点的数据库,维护起来要比单机的PostgreSQL要复杂一些,但是很多方面其实跟PostgreSQL大同小异。以下是一些入门级别的实践小结。使用版本9.5。

集中式管理

Postgres-XL由gtm、gtm proxy、datanode、coordinator四种功能节点组成,就算只有2个datanode、2个coordinator,主机数量也可能达到五个。如果再配上standby节点、因请求压力增大而增加datanode数量,集群环境很容易突破十多个主机。这样一个不大不小规模的Postgres-XL要是经常开十多个窗口上去维护也会把人累个半死,并且对于一个发展中的小公司而言,这样的集群可能不止一个。所以,如果能在一个中心控制台远程操作所有主机,执行一些常用的简单的命令,将是一件省力省时的好事。

常见操作归纳如下:

  1. 初始化各功能节点
  2. 启动、停止各功能节点
  3. 统一修改datanode和coordinator的配置文件,并reload使其生效。
  4. 查看各功能节点的运行状态。
  5. 增加、移除datanode和coordinator节点。
  6. 失效转移datanode和gtm节点。
  7. 重新构建datanode和gtm的standby节点。
  8. 在datanode节点间均衡数据,即把新增的datanode添加到各个分片表中,或把将要移除的datanode从各个分片表中删除。
  9. 在所有datanode和coordinator节点上执行sql语句。
  10. 查询和清理datanode和coordinator节点的操作日志。
  11. 清理datanode和coordinator节点的归档的预写日志(WAL)。
  12. 对datanode和coordinator节点执行基础备份。
  13. 回滚datanode上残留的预提交事务。
  14. 执行vacuum freeze,防止事务ID回卷。
  15. 执行create barrier,创建各节点一致的用于恢复的事务提交barrier。

以上功能可以使用shell或python脚本语言实现,通过免密码SSH来操控各主机。另外配置项中的归档命令(archive_command)和恢复命令(restore_command)有可能使用scp或rsync,所以相关主机间也要支持免密码SSH。简单起见,所有主机包括控制台主机的postgres账户使用相同的密钥对,彼此都可以免密码SSH。

配置项调优

参考https://yq.aliyun.com/articles/64871
参考https://github.com/willwang1208/LinuxLearn/tree/master/postgres-xl/pgxl_ctl/v2.0/etc

备份与恢复

线上服务主要采用了三种备份机制。

# 流复制热备

  1. 目的:
    保障生产环境高可用。当主节点宕机时,可以快速将处于热备状态的从节点升级为主节点继续提供服务。
  2. 建立热备的过程:

    主节点的postgresql.conf文件增加WAL的归档相关的配置项并重启:

    # hot_standby用于standby节点可提供只读服务,但是PGXL的standby不支持从GTM获取xid,所以不能提供只读
    archive_mode = on
    #wal_level = hot_standby
    wal_level = archive
    # 归档命令,后期修改
    #archive_command = 'rsync %p postgres@dn-h1.pgxl.com:/data/pgxl_alog/datanode1/%f'
    archive_command = 'scp -P 22 %p postgres@dn-h1.pgxl.com:/data/pgxl_alog/datanode1/%f'
    # 最大WAL发送进程数量
    max_wal_senders = 8
    

    从节点用pg_basebackup命令制作基础备份,然后在数据目录下增加recovery.conf文件,内容如下:

    # 设置standby模式和primary连接信息,该节点将以流复制(Streaming Replication)的方式从primary处实时同步最新的WAL
    standby_mode = on
    primary_conninfo = 'host = dn-m1.pgxl.com port = 23001 user = postgres application_name = datanode1'
    # 拷贝归档的WAL文件到pg_xlog目录下准备恢复
    #restore_command = 'cp /data/pgxl_data/dn/pg_alog/%f %p'
    restore_command = 'scp -P 22 postgres@dn-h1.pgxl.com:/data/pgxl_alog/datanode1/%f %p'
    # 清理归档的WAL命令,也可自行清理,不依赖这个过程
    #archive_cleanup_command = 'pg_archivecleanup /data/pgxl_data/dn/pg_alog %r'
    archive_cleanup_command = '/bin/date'
    

    从节点启动后会进入恢复状态。首先从归档路径拷贝WAL文件到本地进行回放,直到追赶上最新的尚未归档的WAL文件(该文件在归档目录下找不到)。可以在主机上看到类似postgres: startup process recovering xxxxxxxxxxxxxxx这样的回放WAL的进程,该进程会一直存在。

    当回放进程追上最新的WAL文件,即在归档目录找不到下一个WAL文件时,开启流复制进程。可以从主机上看到多了一个类似postgres: wal receiver process streaming 2E2/B9D25C28这样的进程。该进程把主节点最新WAL的动态实时更新到本地,之后回放进程持续进行恢复。

# 基础备份 + 归档预写日志

  1. 目的:
    保障在发生误操作或数据损坏后,能够回退到最近的某个时刻。
  2. 方法:

    每隔X小时制作一次基础备份,保存到专职备份的主机上,保留最近的N份。WAL的归档目录也在这个主机上。主节点持续向这里归档完成的WAL,保留最近的M个文件。从节点在初始化时也从这里拉取需要的WAL文件。

    基础备份在热备中也用到,用pg_basebackup命令完成。pg_basebackup过程实际上是首先在主节点执行SELECT pg_start_backup(‘label’),之后会产生一个类似00000001000002E20000005A.0007C2C0.backup这样的WAL文件,这是一个备份开始的标记。然后通过cp等方式对整个数据目录进行物理备份。完成后再在主节点执行SELECT pg_stop_backup()结束backup模式。由于默认情况下,开始备份会触发一次checkpoint,备份会等待这次checkpoint完成才继续。而checkpoint时间有可能比较长,为了能立即开始备份可以使用SELECT pg_start_backup(‘label’, true)命令,使checkpoint进程忽略checkpoint_completion_target参数,以最快速度完成。这在pg_basebackup脚本中可以通过-c fast来设置。但是此设置会在短时间内消耗大量IO,影响系统稳定,如果在非访问低谷时段这样做,得不偿失。

    pg_basebackup -p 23001 -h dn-m1.pgxl.com -c fast -D /data/pgxl_data/dn
    

    要想利用某个时间制作的基础备份,把数据库恢复到其后某个时刻,还需要之后产生的所有WAL文件。在主节点,当最新的WAL写满后,通过归档命令把完成的WAL复制到另一个目录下(归档目录)保存。实际上,如果主节点正常stop时,也会把未写满的最新的WAL复制到归档目录,产生一个类似xxxxxxxxxx.partial这样的文件。但如果是非正常情况结束主节点进程,归档目录则不会包含未写满的WAL文件,此时仅仅靠归档目录的WAL恢复数据有可能是不够的,还需要主节点上pg_xlog目录下的未写满的那个WAL文件。

    对Postgres-XL而言,需要为每个datanode分别做备份,coordinator可以依据实际情况决定是否要做。并且需要定期(比如每分钟)执行一次create barrier ‘xxxx’,作为恢复时的恢复点。其中一个datanode的部署图如下:

    postgres-xl-1001

  3. 恢复:

    如果生产环境发生了意外,需要回退就会用到这些备份文件和WAL。

    首先确定案发时间,选择一个案发之前的最近的基础备份,替换生产环境中的数据目录,增加recovery.conf文件内容如下:

    # 回放WAL截至的时刻
    recovery_target_time = '2017-11-11 11:11:11'
    # 拷贝WAL的命令
    restore_command = 'scp -P 22 postgres@dn-h1.pgxl.com:/data/pgxl_alog/datanode1/%f %p'
    

    然后启动,查看日志等待恢复完成,完成后recovery.conf文件会变成recovery.done。

    对Postgres-XL而言,如果要回退,则所有datanode都要回退,且回退到相同的事务状态。但是由于各datanode上事务的提交时间和先后顺序可能不一致,所以不能通过recovery_target_time来保证节点间的一致性。为了解决这个问题,Postgres-XL引入了CREATE BARRIER barrier_name命令。该命令在各节点上产生的xlog时,节点间的事务提交状态是一致的。之后在恢复时用recovery_target_barrier = ‘barrier_name’替代recovery_target_time即可。

# 逻辑导出备份

  1. 目的:

    用于导入全部或部分数据到测试环境,做测试或其他目的。

  2. 方法:

    每日定时执行导出脚本。注:在Postgres XL中,只有所有datanode和coordinator节点都可用时才能运行pg_dumpall或pg_dump命令。

    逻辑备份恢复方法一:

    # 导出
    /usr/local/pgxl/bin/pg_dumpall -h 172.30.8.123 -p 21001 -U postgres --clean --if-exists --inserts | gzip > monster_all.sql.gz &
    # 导入
    gunzip -c monster_all.sql.gz | psql -p 21001
    gunzip -c monster_all.sql.gz | /usr/local/pgxl/bin/psql -h 172.30.8.221 -p 21001 -U postgres >import.log 2>&1 &
    

    逻辑备份恢复方法二:

    # 导出schema
    /usr/local/pgxl/bin/pg_dumpall -h 172.30.8.123 -p 21001 -U postgres --clean --if-exists -s | gzip > monster_all_schema.sql.gz &
    # 导出各database
    /usr/local/pgxl/bin/pg_dump -h 172.30.8.123 -p 21001 -U postgres -ab -j 4 -F d -f mfsso mfsso &
    /usr/local/pgxl/bin/pg_dump -h 172.30.8.123 -p 21001 -U postgres -ab -j 4 -F d -f mfpay mfpay &
    /usr/local/pgxl/bin/pg_dump -h 172.30.8.123 -p 21001 -U postgres -ab -j 4 -F d -f monsters monsters &
    # 导入schema
    gunzip -c monster_all_schema.sql.gz | /usr/local/pgxl/bin/psql -h 172.30.8.221 -p 21001 -U postgres >import.log 2>&1 &
    # 导入一个database
    /usr/local/pgxl/bin/pg_restore -h 172.30.8.221 -p 21001 -U postgres -a -j 4 -d mfsso mfsso >import.log 2>&1 &
    

定时任务

crontab脚本如下:

# 清理节点日志
0 0 * * * /bin/bash /usr/local/pgxl_ctl/pgxl_ctl.sh -m cleanlog -z all -c 10
# 清理归档的预写日志
*/5 * * * * /bin/bash /usr/local/pgxl_ctl/pgxl_ctl.sh -m cleanachlog -z datanode -r backup -c 3072
# 生成基础备份
15 3,9,15,22 * * * /bin/bash /usr/local/pgxl_ctl/pgxl_ctl.sh -m basebackup -z datanode -r backup -c 10
# 冻结事务ID
0 19 * * * /bin/bash /usr/local/pgxl_ctl/pgxl_ctl.sh -m freeze
# 生成恢复点
*/1 * * * * /bin/bash /usr/local/pgxl_ctl/pgxl_ctl.sh -m barrier -c $(date +'%Y%m%d-%H%M%S')

监控

这里指的是除了基础设施和操作系统级的监控外,对于Postgres-XL的数据库级的一些指标的统计,需要安装pg_stat_statements扩展组件。统计内容包括但不限于:

  1. 单位时间内完成的事务数。
  2. Top耗时、耗IO、抖动大的SQL语句。
  3. 顺序扫描执行次数多且扫描行数多的表。
  4. 当前发生死锁的进程,由于当前版本的Postgres-XL对死锁的处理不完善,所以要监控此项。
  5. 表膨胀和索引膨胀情况。
  6. 表大小和数据量增长情况。

参考工具postgres-xl-statistics

节点故障自恢复

采用的方法是,单点运行监控脚本,每隔N秒观察一次集群各节点的健康状况(目前是用nmap检查端口的方式,不能辨别端口在但是服务不可用的情况。例如datanode崩溃重启后的recovering过程中。可考虑用pg_isready检测),根据不同情况进行restart或failover。小规模集群可用。

遇到的问题:

  1. gtm standby只在init的时候同步master的gtm.control(保存全局事务ID和全局序列信息),并且只同步了事务信息,不包括序列信息。如果直接failover到standby节点会报错如下,

    3:2633996032:2018-01-16 14:36:31.592 CST -LOG:  The sequence with the given key does not exist
    LOCATION:  GTM_SeqGetNext, gtm_seq.c:947
    4:2633996032:2018-01-16 14:36:31.592 CST -ERROR:  Can not get current value of the sequence
    LOCATION:  ProcessSequenceGetNextCommand, gtm_seq.c:1591
    

    为了让standby上的gtm.control与master的“实时”同步,需要额外的文件同步支持,以保证master宕机后standby上的数据是完整有效的。

    gtm启动时会自动把next_xid和global_xmin增大50000多,序列的nextval增大2000,可能是为了让其一定大于启动后各节点上报的数字(序列的nextval会根据上报的数字重新调整)。所以即使standby上的gtm.control稍有滞后也没关系,只要增加增量后的值大于上报值即可。

    gtm standby节点通过gtm promote提升为master后,可能连不上,报错ERROR: cache lookup failed for node。所以最好直接restart。

  2. 无论是gtm master重启,还是gtm standby提升为master后,都需要让gtm proxy重连gtm。

    有两种重连方法,一是用gtm_proxy reconnect,这会在gtm_proxy的数据目录下生成名为newgtm的文件,保存上次reconnect命令的-o内容,但是gtm_proxy正常启动不受此文件影响,只加载gtm_proxy.conf中的配置。二是直接gtm_proxy restart,如果是gtm standby提升为master,先修改gtm_proxy.conf的gtm配置。推荐使用后者。

  3. gtm master重启后,如果不重启datanode,会报错如下,

    1:1822299904:2018-01-17 12:30:48.680 CST -LOG:  GTM_ERRCODE_NODE_NOT_REGISTERED - node_name datanode1
    LOCATION:  GTM_HandleGlobalXmin, register_common.c:967
    1:494905088:2018-01-17 12:30:48.746 CST -LOG:  GTM_ERRCODE_NODE_NOT_REGISTERED - node_name datanode2
    LOCATION:  GTM_HandleGlobalXmin, register_common.c:967
    

    具体有何影响尚不知晓,重启datanode后解决。

  4. datanode standby提升为master时,使用命令pg_ctl promote -D /data/pgxl_data/dn完成。这会使standby节点的xlog的timeline加1,如图,

    pgxl-ha2-001

    如果直接将其改回standby用recovery模式启动,他将从00000002…开始恢复,由于原归档目录下没有加1后的xlog,日志中会提示highest timeline 1 of the primary is behind recovery timeline 2。

    datanode standby提升后,旧master不能再作为master启动,否则会扰乱gtm。旧master需要rebuild成新的standby启动。

  5. 当高压情况下,gtm或datanode崩溃,可能造成各datanode上残留不能被正确处理的预提交事务,查看pg_prepared_xacts。这也会导致gtm上报错如下,

    33:3330262784:2018-01-12 17:58:16.868 CST -LOG:  GTM_ERRCODE_TOO_OLD_XMIN - node_name datanode2, reported_xmin 447555, previously reported_xmin 1030741, GTM_GlobalXmin 1030741
    LOCATION:  GTM_HandleGlobalXmin, register_common.c:1038
    

    通过rollback解决。

Creative Commons License

本文基于署名-非商业性使用-相同方式共享 4.0许可协议发布,欢迎转载、使用、重新发布,但请保留文章署名wanghengbin(包含链接:https://wanghengbin.com),不得用于商业目的,基于本文修改后的作品请以相同的许可发布。

发表评论