MySQL高可用集群方案之PXC
1. 前言
在之前的公众号文章《 在游戏运维实战中摸索前行的“异地双活”架构 》中,我们向大家介绍了我们游戏中对“异地双活”架构的实践历程。
其中为了实现DB层的多点写入及数据强一致性,我们结合实际的业务场景测试对比了Percona Xtradb Cluster和MySQL Cluster。
我们选择最新版的sysbench 0.5版本来测试数据库性能。南北机器分别并发20、60、100个进程进行压测。以下是压测对比的结果:
从测试结果上看,大数量和大并发的场景下,MySQL Cluster集群的整体性能都越优于Percona Xtradb Cluster集群。这需要得益于MySQL Cluster基于内存的引擎NDB。但NDB引擎耗内存、易崩溃、运维成本巨大。而反观Percona Xtradb Cluster,节点与传统MySQL实例基本无异,日常运维也是差不多的。在性能方面我们可以借助SSD+RAID10提高IO性能,异地机房借助专线以降低网络抖动。基于实际场景,我们允许集群本身有一定的性能牺牲,然后借助底层硬件的提升来弥补,最终我们采用了Percona Xtradb Cluster。
今天,笔者向大家具体介绍Percona Xtradb Cluster,以及我们的经验总结。
2. 集群介绍
PerconaXtradb Cluster是一种完全开源的MySQL高可用解决方案,它将Percona Server、Percona XtraBackup与Galera库集成在一起,以实现同步多主复制的MySQL集群。
- Multi-master的架构,数据多点写入并自动同步到集群。
- 本地执行查询,所有节点都具备完整的数据副本,查询涉及本地数据。
- 基于验证的复制机制,数据写入需要集群所有节点验证通过了才会提交,保证了数据的强一致性。
- 去中心化架构,集群节点可自由伸缩,脱离集群的节点可以将其转为普通MySQL实例的角色提供服务。
PerconaXtradb Cluster作为一种出色的MySQL高可用解决方案,满足了现有业务架构的需求,但其劣势也是很明显,在使用前一定要摸清其短板。Percona Xtradb Cluster主要有以下限制:
- 集群的复制只支持 InnoDB 引擎。
- 集群吞吐量取决了性能最差的节点。
- 不支持InnoDB fake changes和XA事务。
- 不允许大事务,事务提交的最大行数及大小受制于变量: wsrep_max_ws_rows 和wsrep_max_ws_size variables。
- 不支持锁表:LOCK TABLES、UNLOCK TABLES,不支持锁表函数: GET_LOCK()、RELEASE_LOCK()
- 乐观锁并发控制,事务提交时仍可能在该阶段被中止。
3. 基础概念
除了摸清短板,要想玩好Percona Xtradb Cluster,还需要对集群的基础概念有一定的了解才好办。本节,笔者先给大家讲讲Percona Xtradb Cluster的一些重要基础概念。
3.1 常见术语
- IST:数据增量同步,节点加入集群的数据同步方式
- SST:数据全量同步,节点加入集群的数据同步方式
- write-sets:事务写集
- Donor节点:新节点加入集群时,集群会投票选出Donor节点提供新节点所需的数据
3.2 主要端口
- 3306:节点对外提供服务的端口
- 4567:节点间相互通信及复制的端口
- 4444: SST使用的端口,Joiner节点以SST方式加入集群时启用该端口接受数据(Donor节点会启动随机端口主动来连接4444端口)
- 4568: IST使用的端口,Joiner节点以IST方式加入集群时启用该端口接受数据(Donor节点会启动随机端口主动来连接4568端口)
3.3 grastate.dat
grastate.dat是节点状态记录文件,它一个非常重要的文件。节点故障恢复的第一步就是恢复这个文件,节点重新加入集群第一个需要关注的也是这个文件。这个文件位于数据目录下。该文件的内容如下:
- uuid:节点所属集群的uuid
- seqno:节点正常关闭时最后提交的事务编号,如果集群非正常关闭或者正在运行状态,seqno的值为-1
- safe_to_bootstrap:集群最后一个关闭的节点的值为1,其它为0
节点重新加入集群时,会从grastat.dat中的seqno开始做IST。若这个文件不存在或者损坏了,那么意味着节点将会以SST的方式加入集群,这是我们最不愿见到的。
3.4 GCache
GCache是一种环形缓冲区,主要由内存、内存映射文件和物理文件组成。在节点运行期间,GCache缓存着最新的写集。Donor节点可以直接将GCache中最新增量数据给Joiner节点,实现Joiner节点快速加入集群。
GCache的使用顺序:优先使用内存,再使用映射文件,都用完了同时还是没有PURGE出空间来,则使用物理文件。
GCache的大小决定了节点可以离开集群多长时间重新以IST的方式加入集群。
3.5 重要的状态变量
- wsrep_local_state_comment:节点当前的状态
- wsrep_cluster_status:集群当前的状态
- wsrep_local_cached_downto:当前节点gcache中缓存的最小事务编号
- wsrep_last_committed :当前节点已提交的事务编号
4.基本维护
4.1 新集群的启动方式
第一个节点先初始化,再以bootstrap方式启动:
# 初始化 /usr/local/mysql/bin/mysqld --initialize --datadir=/data/database/mysql/ --basedir=/usr/local/mysql/ --user=mysql # 启动 /usr/local/mysql/bin/mysqld_safe --defaults-file=/etc/my.cnf--wsrep-new-cluster &
其他的节点无需初始化,直接按普通MySQL实例的方式启动,会自动加入集群。
4.2 集群全部关机后如何启动
原则:维护前最后关闭的节点,维护后以bootstrap的方式第一个启动
/usr/local/mysql/bin/mysqld_safe –defaults-file=/etc/my.cnf –wsrep-new-cluster&
Q:怎么知道哪个节点是最后关闭的?
A:从前面我们可以知道,grastate.dat文件中的safe_to_bootstrap字段,集
最后一个关闭的节点的值为1,其它为0。因此可以根据grastate.dat文件判断。
4.3 节点重启
节点不管以哪种方式离开集群,需要以IST的方式重新加入集群,都受到GCache大小的限制。在重新加入集群时,都不可直接启动节点,需要检查grastate.dat文件和对比集群中其他节点的“wsrep_local_cached_downto”状态,已确定是否能IST加入,具体我们在后面细说。
4.4 节点备份
使用xtrabackup/innobackupex进行全库备份时,加上–galera-info参数,备份会生成xtrabackup_galera_info文件。做故障恢复或者新节点加入时,可以直接利用这个文件重建grastate.dat。
xtrabackup_galera_info文件内容包含了集群UUID和备份时节点最新提交的事务编号,如下:
bf26341f-43cb-11e8-a863-62c0eb4d9e79:529045
5.常见故障处理
PerconaXtradb Cluster集群节点数推荐是3个节点,我们线上也采用3节点的部署方案。这里笔者就以3节点的集群作为说明。
5.1 节点进入非主模式
PerconaXtradb Cluster为了保证数据一致性,当某个节点脱离集群,或者发生脑裂时,节点数小于50%的子集群会进入非主模式拒绝对外提供服务。
如上图所示,AB两个节点故障宕机,集群只剩C节点,而C节点与另外两个节点失联认为自己脱离了集群而进入非主模式,这时候C节运行正常,但不允许执行任何SQL,如下报错:
ERROR 1047 (08S01): WSREP has not yet prepared node for applicationuse
这时候紧要急恢复业务,可以先让C节点允许访问,然后再恢复AB两个节点。可以在C上执行:
SET GLOBAL wsrep_provider_options=‘pc.bootstrap=true’;
这样就可以让C节点对外提供正常的读写服务了。
5.2 节点互相失联
如上图所示,因中间交换机的故障导致ABC三个节点相互失联,所有节点都进入非主模式,所有节点正常运行却都不可访问。为了紧急恢复业务,我们需要优先紧急恢复其中一个节点提供服务。但是这三个节点都正常运行,我们恢复哪个节点提供服务呢?从前面的介绍可以知道,节点的状态变量wsrep_last_committed 表示当前节点已提交的事务编号,我们可以让已提交事务最新的节点恢复服务:
5.3 节点宕机恢复思路
由于集群每个节点都有相同的数据副本,我们并不担心节点故障宕机会导致数据丢失。我们需要思考的是,节点故障宕机后如何正确恢复并重新加入集群。
对于故障节点,我们可以用xtrabackup/innobackupex拉取数据做IST加入集群,或者借助备份,甚至任性点清空故障节点的数据目录直接启动做SST加入。不过,相信不管是运维还是DBA,都希望直接利用本地已有的数据做IST恢复吧。
本小节,我们先给出故障节点如何以IST的方式重新加入集群的思路,后面做具体的实践。思路如下:
- 恢复grastate.dat文件
- 确定能否以IST的方式加入集群,既找到donor节点
- 不存在合适的donor(无法IST),先追binlog,再IST
5.4 恢复grastate.dat文件
从前面对grastate.dat文件的说明可以知道,节点重启时需要从grastate.dat中读取seqno做IST。因此,故障恢复的第一步就是恢复grastate.dat文件。
恢复具体步骤:
1、直接启动服务,生成wsrep_recovery日志。
这时候启动会启动失败,因为mysqld进程因为找不到seqno而进行recovery,终端会打印如下内容:
mysqld_safe WSREP: Running position recovery with–log_error=‘/data/database/mysql/wsrep_recovery.5UKe8s’
2、借助wsrep_recovery日志,找到seqmo:
grep “Recovered” /data/database/mysql/wsrep_recovery.5UKe8s2018–04–03T08:18:54.534461Z 0 [Note] WSREP: Recovered position: bf26341f-43cb-11e8-a863-62c0eb4d9e79: 529045
3、重建grastate.dat文件:
# GALERA saved state version: 2.1uuid: bf26341f-43cb-11e8-a863-62c0eb4d9e79 seqno: 529045safe_to_bootstrap: 0
到现在,一个宕机节点的grastate.dat文件就恢复好了。目前这个节点的状态如同一个正常关机的节点一样。但这不代表启动就一定会以IST的方式加入集群。
5.5 确定Donor节点
从集群中确定一个节点为donor节点,目的就是为了让重新加入集群的节点能从donor节点获得增量数据,以IST的方式加入集群。
1、查看集群现有节点gcache中最小事务编号:
2、 哪个节点gcache缓存中最小事务编号小于新加入节点的seqno,说明该节点的gcache缓存有故障节点宕机期间产生的所有事务。由上面的两个截图可以看出30.0.0.226(pxc-node-0)可以确定为donor节点。
3、指定donor节点启动:
/usr/local/mysql/bin/mysqld_safe --defaults-file=/etc/mysql.cnf--wsrep_sst_donor=pxc-node-0 &
到此,一个故障节点就恢复了,IST加入集群这个过程会非常快。但如果故障节点停机过长,或者数据量过大导致gcache不足以缓存停机期间的write-sets,那么就找不到donor节点而无法做IST。怎么办?
5.6 Binlog追数据
对于停机时间较长的节点,可能集群所有节点gcache缓存的write-sets都小于停机期间产生的write-sets,这时候停机节点将无法直接IST加入集群。不过可以借助binlog追数据,使停机节点的数据尽可能靠近集群,再做IST。
1、 借助已恢复的seqno找到其他节点的binlog的start-position:
如果停机期间,集群生成了多个binlog,那么就需要借助mysqlbinlog工具定位到具体的binlog文件。这里假设是mysql-bin.0000015:
mysqlbinlog -vv mysql-bin.0000015| grep -A5 “Xid = 529045”
这里用到的特性就是Xid的值,也就是GTID值的后半部分,在集群中同一个事务对应的Xid值是完全一样并且有序的,只要找到Xid的值,就可以在其它节点中找到对应位置及相关信息。
2、导出binlog:
mysqlbinlog –skip-gtids=true –start-position=388603320 -vv mysql-bin.0000015 > data.sql
这里需要借助–skip-gtids=true参数使解析出来的文件中就不包含“SET@@SESSION.GTID_NEXT=”,否则可能会binlog导入失效。
3、非主模式启动节点
这里让故障节点以非主模式启动,是为了和集群保持一致的uuid,同时又能独立启动以导入binlog。如下操作:
grastate.dat文件做如下修改:
safe_to_bootstrap: 1
独立启动:
/usr/local/mysql/bin/mysqld_safe --defaults-file=/etc/my.cnf --wsrep-cluster-address="gcomm://" &
关闭非主模式,导入数据:
mysql> SET GLOBAL wsrep_provider_options='pc.bootstrap=true'; mysql> source data.sql ;
这里除了要导入data.sql,还需要导入mysql-bin.0000015之后的binlog,让故障节点尽可能追近集群。
关闭实例并还原grastate.dat配置:
safe_to_bootstrap: 0
4、正常启动实例
通过binlog,故障节点已经恢复到最接近集群的状态了。如果担心,也可以再检查是当前状态是否满足以IST方式加入集群,毕竟导binlog的过程也是很花时间的。
6.总结
文章对Percona Xtradb Cluster的介绍,比较浅显,更深入的探索,是需要结合业务场景进行钻研。
站在业务的角度,Percona Xtradb Cluster多点写入、数据强一致的特点确实十分诱人。站在运维的角度,Percona Xtradb Cluster需要花一番功夫:节点的性能、中间链路的稳定性、集群调优等,都是不小的一笔运维成本。展望未来,我们对Percona Xtradb Cluster仍需不断深入实践,从游戏的异地双活,再到平台级的多活。