今天在测试环境遇到一个很有意思的问题,我们在测试一款分布式数据库,这款分布式数据库底层是基于postgresql做的,现象大致是这样的,我们在重启数据库集群后发现某台机器的数据节点启动失败,然后去那台机器上发现进程确实没有启动。然后看了下运行日志,定位到了启动失败的原因,是内存不够用了,查了下该节点上还运行着其他数据库,由于其他数据库也占了一部分内存,造成该节点数据库由于内存不足启动失败。
上面说的启动失败只是一下一个导火索,真正的异常发生在后面,启动失败后查询了一下集群状态,发现该数据节点的主节点状态为down,数据节点目前是一主两备的架构,使用quroum作为一致性协议,那么三节点的状态下只要有一个备返回就可以提交。
集群管理软件检测到数据节点A主节点down后会自动触发切换,选举出一个备节点B接替主节点,切换完成后有意思的事情出现了,查看集群状态发现原来的A主节点和C备节点处于standby rebuilding状态,因为看到rebuilding,第一感觉是数据节点重建了,这时脑子里第一反应是去A机上去看看数据目录大小,看是否真的重建了,果然,数据目录一直在增长,最终两个节点重建成功,主备关系恢复,整个过程都是集群管理自动完成,没有人工参与,而且在备节点重建过程中业务可以正常进行。
这里有个值得思考的问题,重启后主节点无法启动备节点升主是意料之内,但是切换后为什么会做rebuilding?后来思考了下,终于想通了。
我们先来看看postgresql的三种停止模式:smart/fast/immediate
Smart:等待所有活跃的会话及备份关闭,类似于oracle的shutdown normal
Fast:服务器不会等待客户端连接关闭而是直接终止,所有未提交的事务会被回滚,这也是默认的停止模式,类似于oracle的shutdown immediate
Immediate:强制关闭,类似于断电,在实例重新启动时会做实例恢复,类似于oracle的shutdown abort
再回到我们的案例,我们可以先想象一下异常停止集群。如果异常停止,那么就可能出现残留信息,master重新启动时需要利用之前的wal日志进行实例恢复,这部分wal是没有传递到备机的,也就是说主机多一部分残留的日志,这时启动集群如果正好A及master由于某种原因(这次是内存原因)无法启动,那么触发切换后新的master B机就比原master A机多一些日志,这样的话b机产生的wal如果传给a机可能造成不一致。这时集群管理为了保证一致性将原master A机进行rebuild也是合情合理,rebuild过程中B和A的主备关系失效,所以B机升主后其实是在裸跑,不会影响业务,待原A机rebuild完成后和B机的同步关系恢复。
上面我们大致分析了一下原因,但其实我停止集群的时候并没有使用immediate方式异常停止,而是正常停止的。按说正常使用fast模式停止集群,会进行checkpoint,同时wal会传到备机,应该不会产生主机多日志的情况,这又是什么原因呢?
后来细想一下发现,因为是分布式的,集群停止时是按照stop node1.2.3.4.5.6来下发停止命令的,而我的A机是最后一台机器node6,B机和C机是前面的两台,所以说B机和C机可能会先停止,而集群的机制是为了保证高可用,在备数据节点宕机主数据节点存活时,依然不影响业务,主节点依然可以写入,所以当BC机停止后这时A机其实还在写入并产生wal日志,但是已经无法同步给BC的slave节点了。这样就造成master比slave的数据多,这时进行切换就需要将原master重建。
技术人要有精益求精的精神,探究技术细节并且记录它也是一件很有意思的事情,加油吧。
更多精彩文章请关注我的公众号:数据库架构之美