
文/张馨鹏
MySQL是由瑞典MySQL AB公司自1995年开始开发的一款关系型数据库,目前属于Oracle旗下产品。根据权威数据库排名机构DB-Engines的统计数据,MySQL数据库在开源数据库中排名第一。
使用开源数据库MySQL实现数据库技术的自主可控是一个很好的选择,我行也在开源数据库上进行积极的探索和试用。
在考量数据库集群架构时,能否保证数据库节点间数据一致和良好的性能是数据库必须解决的问题。因为集群架构下节点间存在网络时延、带宽限制、性能差异等问题,造成数据一致性和高性能是鱼和熊掌不可兼得,这就要求数据库设计者要在数据一致性和集群性能之间找到平衡。
为了解决这些问题,MySQL原生提供了主从复制,组复制(MGR)两种数据复制模式。
MySQL主从复制是基于主库节点的操作日志(binlog)复制到从库节点的中继日志(relay)再回放实现数据的一致性。基于数据库性能的考量,有异步复制和半同步复制两种情况。

异步复制不能保证节点间数据一致性,适用于对数据一致性要求不高的场景。半同步复制虽然能在正常情况下保证数据一致性,但也存在因为网络抖动等原因退化为异步复制的风险。
虽然可以通过参数设置强制不退化为异步来保证一致性,这将会造成数据库假死情况影响性能,严重时会造成对应用不可用。同时主从复制是单主模式,也不能进行自动的故障转移,需要人工干预切主和数据恢复等操作。
组复制(MGR)架构跟随MySQL 5.7.17发布,放弃了主从复制模型的日志复制方式,采用分布式一致性Paxos协议,如下图所示,在Consensus阶段需要所有节点表决是否已经将主的操作日志落盘,只有当超过半数节点日志落盘,主节点的事务才能提交。

组复制(MGR)的投票机制解决了数据一致性问题,也兼顾了性能,且同时支持单主模式和多主模式。在多主模式下,所有节点都支持读和写操作,同时MGR集群内部可以在异常场景下自动切换恢复,极大的提升了高可用性。
虽然,组复制在高可用、数据一致性和性能方面都很不错,但是,因为其缺少路由层,对于应用来说,异常场景的自动切换仍是黑盒操作,比如主节点的切换应用是无法感知的。另外,组复制作为MySQL的插件存在,默认是不加载的,在环境部署的时候,操作较为繁琐。
MySQL InnoDB Cluster架构跟随MySQL 8发布,在MySQL Group Replication(MGR)基础上新增MySQL Router和MySQL Shell组件来实现集群路由和管理。至此形成一套完整的高可用架构,架构如下图所示:



MySQL Group Replication(MGR):基于分布式一致性Paxos协议的组复制技术,解决数据库的扩展、自动故障转移以及事务一致性。
MySQL Router:原生轻量级中间件,提供应用程序连接数据库的故障转移与路由切换,可在应用程序和后端MySQL服务器之间提供透明路由。
MySQL Shell:实现MySQL客户端和命令编辑功能,除了支持SQL功能还支持JavaScript和Python代码编码功能。可根据提供的API实现自定义功能,为系统运维提供便利。主要运用于MySQL InnoDB Cluster架构的系统检查、参数设置与集群关系维护等。

投票机制
在多节点分布式架构下常用投票机制实现强一致性和性能的平衡。MGR采用的Paxos协议可以保障数据库兼顾数据一致性和性能从而实现高可用。
简单来说,数据库事务是否提交由节点间投票完成,当超过半数节点表明完成,主节点事务才能提交。此机制保证了高可用场景的数据一致性。
并行回放
在MGR架构中,默认情况下从节点对数据回放是由单线程完成,导致随着业务压力的增大主从节点延迟变大,这对性能有较大的影响。因此MySQL加入了日志的多线程并行回放功能解决该问题。随着而来的问题是,如何切割日志来并行回放才能保证数据一致性?MySQL提供了以下两种并行回放模式:
1)DATABASE:基于数据库级别的数据回放。也就是一个回放线程只能对一个数据库操作。但目前大多数使用MySQL都采用单库多表,这种场景下相当于仍是单线程串行回放。
2)LOGICAL_CLOCK,基于逻辑时钟复制。考虑到在主库上不冲突的事务也是并行执行,如果我们按照逻辑时钟对事务进行分组编号进行切分,也就不会产生事务冲突影响数据一致性。这样可以在同一个数据库中并行回放relaylog,极大地提高性能。
消息分包传递
如何处理大事务是数据库绕不过的一道坎。这里要说下MGR中使用的通讯组件XCom。XCom负责节点间消息的发送与接收,并保证消息投递的一致和有序。消息包括事务相关信息和心跳统计相关信息。但因Xcom是单线程实例,在处理大事务必然会影响其他消息的处理,如果是来自其他节点的心跳消息无法回应,超时无响应的节点将会被踢出集群。
因此MySQL 8.0.16版本新增了消息分包功能,可以将大数据包按照设置的最大数据包阀值(默认10MB)进行切分发送,到达后再进行数据组装,增强对大事务支持。
流量控制
在多节点数据库环境下,节点间数据复制同步受网络、硬件限制等多方面因素影响。如果某个节点读写性能差,无法跟上其他节点,造成节点间数据差距越来越大。Paxos协议虽然能保证数据的一致性,但随着数据差变大,较慢的节点会退化为异步复制,严重时造成脱离集群。
因此,MGR引入了流量控制功能。当存在节点写入过慢,触发了流控阀值,将限制主节点的写入速度,保持集群内节点间流量相当。因此,在流量控制下,MGR性能由最差节点决定。
前期准备

1.根据Paxos协议要求,高可用架构下数据库节点数为奇数个。这里我们使用三台服务器。配置IP及hostname,命令如下:
vi /etc/hosts10.XXX.XXX.25 Server-0110.XXX.XXX.26 Server-0210.XXX.XXX.27 Server-03

2.完成单机MySQL、MySQL Router和MySQL Shell安装。

3.创建集群用户并授权。集群用户是集群内部使用,应用需要单独建立用户。
#创建集群用户clusterusercreate user 'clusteruser'@'%' identified by 'clusterpassword';#赋予集群用户必须权限grant RELOAD, SHUTDOWN, PROCESS, FILE, SELECT, SUPER, REPLICATION SLAVE, REPLICATION CLIENT, CREATE USER,SYSTEM_VARIABLES_ADMIN,PERSIST_RO_VARIABLES_ADMIN on *.* to 'clusteruser'@'%' with grant option;grant ALTER, ALTER ROUTINE, CREATE, CREATE ROUTINE, CREATE TEMPORARY TABLES, CREATE VIEW, DELETE, DROP, EVENT, EXECUTE, INDEX, INSERT, LOCK TABLES, REFERENCES, SHOW VIEW, TRIGGER, UPDATE on mysql_innodb_cluster_metadata.* to 'clusteruser'@'%' with grant option;grant INSERT, UPDATE, DELETE on mysql.* to 'clusteruser'@'%' with grant option;#以上clusteruser和userpassword根据需求配置。
架构实现
1.集群用户使用mysql-shell(mysqlsh)登录单节点mysql:
mysqlsh --uri 集群用户@hostname:mysqlport

2.根据上节/etc/hosts示例,登录Server-01示例如下:
mysqlsh --uri clusteruser@Server-01:3306

3.初始化集群节点,mysql-shell自动配置集群参数,过程中授权yes即可,集群所有节点执行。此步骤是检查并打开集群所需参数。
dba.configureLocalInstance()
操作后,显示如下:
Configuring local MySQL instance listening at port 3306 for use in an InnoDB cluster...Some variables need to be changed, but cannot be done dynamically on the server.Do you want to perform the required configuration changes? [y/n]: yDo you want to restart the instance after configuring it? [y/n]: yConfiguring instance...The instance 'Server-01:3306' was configured for InnoDB cluster usage.Restarting MySQL...NOTE: MySQL server at Server-01:3306 was restarted.

4.环境检查。初始化完成后,进行环境检查,这里以Server-01为例,其他三台服务器进行相同操作。
dba.checkInstanceConfiguration('clusteruser@Server-01:3306')
操作后显示如下,则完成基础配置:
The instance 'Server-01:3306' is valid for InnoDB cluster usage.{"status": "ok"}

5.创建集群。创建名为myCluster的集群,集群名可自定义。本步操作任选一节点执行即可,默认以创建集群的节点为主节点。在Server-01中创建集群示例如下:
var cluster = dba.createCluster('mycluster',{autoRejoinTries:10})#参数autoRejoinTries=10表示集群内节点离线自动加入次数,默认5分钟尝试加入一次。

6.集群添加节点。在Server-01中分别添加集群节点Server-02、Server-03,操作如下:
cluster.addInstance('clusteruser@Server-02:3306');cluster.addInstance('clusteruser@Server-03:3306');
选择集群恢复方式,新建集群选择Clone即可:
Please select a recovery method [C]lone/[I]ncremental recovery/[A]bort (default Clone): C
显示如下状态表示添加节点成功:
State recovery already finished for 'Server-02:3306'The instance 'Server-02:3306' was successfully added to the cluster.06');

7.集群状态检查,添加节点完成后,在集群中任意节点执行:
cluster.status()
集群成功建立如下:
{ "clusterName": "myCluster","defaultReplicaSet": {"name": "default","primary": "Server-01:3306","ssl": "REQUIRED","status": "OK","statusText": "Cluster is ONLINE and can tolerate up to ONE failure.","topology": {"Server-01:3306": {"address": "Server-01:3306","mode": "R/W","readReplicas": {},"replicationLag": null,"role": "HA","status": "ONLINE","version": "8.0.18"},"Server-02:3306": {"address": "Server-02:3306","mode": "R/O","readReplicas": {},"replicationLag": null,"role": "HA","status": "ONLINE","version": "8.0.18"},"Server-03:3306": {"address": "Server-03:3306","mode": "R/O","readReplicas": {},"replicationLag": null,"role": "HA","status": "ONLINE","version": "8.0.18"}},"topologyMode": "Single-Primary"},"groupInformationSourceMember": "Server-01:3306"}
自此,MGR复制关系建立。可以看出使用mysql-shell建立MGR关系仅用了5条命令,而且自动检查并开启了MySQL相关服务,相对于以往手动建立MGR过程中:加载插件、修改参数、重启服务等繁琐易错的过程是极大的简便。

8.MySQL Router部署。Router作为路由组件,可以自动感知集群故障切换,自动将应用请求路由转发到正确的节点,实现整个集群对应用的高可用。Router组件轻便,资源利用率低,可根据应用需求部署在应用节点或数据库节点。
以下命令生成MySQL Router配置文件,其中IP、用户名和密码为集群主节点即可:
mysqlrouter --bootstrap clusteruser@x.x.x.x:3306 --directory /mysql_router --user=mysqlrouter --conf-use-sockets
参数说明如下:
--bootstrap :指定InnoDB Cluster集群的主节点--directory:指定配置文件生成目录,需要实现场景或默认--user:指定配置文件拥有人--conf-use-sockets:启动unix sockets
配置过程中需要输入集群实例密码,最终显示如下:
# MySQL Classic protocol- Read/Write Connections: localhost:6446, /mysql_router/mysql.sock- Read/Only Connections: localhost:6447, /mysql_router/mysqlro.sock
启动MySQL Router服务:
cd /mysql_routersh start.sh
MySQL-Router登录验证。默认连接集群读写端口为6446,只读端口为6447。使用应用用户密码以读写模式登录示例:
mysql -utestuser -p -hx.x.x.x -P6446
至此,一个MySQL InnoDB Cluster高可用集群就成功建立起来了。
完

顾问:许国平 李湘宜
赵晓玲 张刚
总编:孙鹏晖
美编:孙鹏晖

长按二维码,关注我们吧!

-本文为“数风云”第2期文章;
-转载本公众号文章请联系我们;
-欢迎来稿:请按“题目-作者”格式命名发送到sunpenghui@abchina.com。




