MySQL数据库高可用方案InnoDBCluster

数据库高可用方案

高可用架构

​ MySQL的高可用架构无论是社区还是官方,一直在技术上进行探索,这么多年提出了多种解决方案,比如 Google的MMM方案,Facebook的 MHA方案,Mysql官方团队提供的MGR 方案等。

MMM 方案

​ MMM(Master-Master replication manager for MySQL)是Google技术团队开发的一款比较老的高可用产品,在业内使用的并不多,社区也不活跃,Google很早就不再维护MMM的代码分支。

在2015年之前,美团公司长期使用MMM架构做数据库高可用。

MHA 方案

​ MHA(MySQL Master High Availability)是由Facebook工程师开发的一款MySQL高可用软件。从名字就可以看出,MHA只负责MySQL主库的高可用。主库发生故障时,MHA会选择一个数据最接近原主库的候选主节点(这里只有一个从节点,所以该从节点即为候选主节点)作为新的主节点,并补齐和之前Dead Master 差异的Binlog。数据补齐之后,即将写VIP漂移到新主库上。

Percona XtraDB Cluster

​ Percona XtraDB Cluster(简称PXC),是由 Percona 公司推出的mysql集群解决方案。特点是每个节点都能进行读写,且都保存全量的数据。也就是说在任何一个节点进行写入操作,都会同步给其它所有节点写入到自己的磁盘。

InnoDB Cluster

​ MySQL官方在2017年4月推出了一套完整的、高可用的Mysql解决方案 - MySQL InnoDB Cluster, 即一组MySQL服务器可以配置为一个MySQL集群。在默认的单主节点模式下,集群服务器具有一个读写主节点和多个只读辅节点。辅助服务器是主服务器的副本。客户端应用程序通过MySQL Router连接到主服务程序。如果主服务连接失败,则次要的节点自动提升为主节点,MySQL Router请求到新的主节点。

MySQL InnoDB Cluster 由以下几部分组成:

  • MySQL Group Replication:向集群的所有成员复制数据,同时提供容错、自动故障转移和弹性。MySQL Server 5.7.17或更高的版本。
  • MySQL Router:确保客户端请求是负载平衡的,并在任何数据库故障时路由到正确的服务器。
  • MySQL Shell:通过内置的管理API创建及管理Innodb集群。
image-20230315210601438

MySQL Shell

​ MySQL Shell 是MySQL 官方团队打造的一个高级客户端,可以对 MySQL 执行数据操作和管理。它支持通过 JavaScript,Python,SQL 对关系型数据模式和文档型数据模式进行操作。使用它可以轻松配置管理 InnoDB Cluster。

​ MySQL Shell是开发人员和DBA的统一接口以及InnoDB Cluster的前端管理器。

常用操作

命令 别名 描述
\help \h 或 ? 打印帮助信息
\quit \q 或\exit 推出MySQL Shell
\ 开启SQL模式下的多行模式
\status \s 显示当前MySQL Shell的状态
\js 切换到javascript执行模式
\py 切换到Python执行模式
\sql 切换到SQL执行模式
\connect \c 连接MySQL实例
\reconnect 重连MySQL实例
\disconnect 断开MySQL连接
\use \u 指定要使用的Schema
\source . 或 source 执行脚本文件
\warnings \W 显示语句产生的警告信息
\nowarnings \w 不显示语句产生的警告信息
\history 查看和编辑历史命令
\rehash 手动更新自动名字缓存
\option 查询和更改MySQL Shell配置选项
\show 使用提供的选项和参数运行特定的报告
\watch 使用提供的选项和参数运行特定的报告,并可按照指定的间隔进行刷新结果
\edit \e 在默认的系统编辑器中打开命令
\page \P 分页配置
\nopager 不进行分页
\system ! 运行操作系统命令

示例:

# 连接mysql
MySQL JS > \connect root@192.168.86.135:33061

dba.checkInstanceConfiguration('root@192.168.86.135:33061')  ---检查节点配置是否正常
# 配置实例时也可以指定要创建的集群管理员账号
dba.configureInstance("root@192.168.86.131:3307",{clusterAdmin:"clusteradmin"})
dba.configureLocalInstance();                         ---检查/etc/my.cnf是否正常

var cluster = dba.createCluster('myCluster');          ---创建一个名字为myCluster的集群
cluster.addInstance('root@192.168.86.135:33061');                ---集群添加节点
cluster.removeInstance('root@192.168.86.135:33061');                  ---移除节点
dba.rebootClusterFromCompleteOutage();                  ---重启集群
cluster.rejoinInstance('root@dkf03:3307')              ---重新加入此节点到集群

var cluster = dba.getCluster();     -- -查看集群名
cluster.status();     ---查看集群状态

cluster.switch_to_single_primary_mode                  ---将集群调整为单主架构 
cluster.switch_to_multi_primary_mode()                 ---将集群调整为多主架构

集群部署

# 下载镜像
docker pull mysql:8.0.32
# 启动容器
docker run -d --name mysql-33063 -p 33063:33063 \
--network=mysql-net  \
-v /var/data/mysql_3/data:/var/lib/mysql \
-v /var/data/mysql_3/conf.d:/etc/mysql/conf.d \
--restart=always -e MYSQL_ROOT_PASSWORD=root mysql:8.0.32
#
docker stop mysql-33061 && docker stop mysql-33062 && docker stop mysql-33063
docker restart mysql-33061 && docker restart mysql-33062 && docker restart mysql-33063

1.MySQL Shell 连接任意一个节点,比如连接 mysql-33061 节点

# 进入容器中
docker exec -it mysql-33061 /bin/bash
# 使用mysql shell
/usr/bin/mysqlsh
# 连接容器中的msyql,也就是mysql-33061
MySQL JS > \connect root@localhost:3306

2.使用 configureInstance 将所有加入集群的节点的参数配置一遍,如果不符合加入集群的要求,MySQL Shell 会自动识别并询问是否一键修改配置参数,修改完成后重启相应的节点就生效了。

dba.configureInstance("root@192.168.86.99:33061",{clusterAdmin:"clusteradmin"})
# 所有节点都配置完成并且重启后,检查一遍,状态返回OK说明没问题
dba.checkInstanceConfiguration('root@192.168.86.135:33061')  ---检查节点配置是否正常

3.三个节点都配置好符合加入集群的参数后,开始加入集群

# 创建一个集群,自动将当前连接的节点加入集群,并且默认是master节点
var cluster = dba.createCluster('myCluster'); 
# 接着将其他两个节点依次加入到集群中
cluster.addInstance('root@192.168.86.99:33062');
cluster.addInstance('root@192.168.86.99:33063');

注:

  • 新节点加入集群后,第一件事就是同步数据,按照Shell 的指示操作就可以;
  • 因为容器之间是网络互通的,所以使用容器名称就可以访问另一个容器的mysql,相当于IP:port

4.查看集群状态:

 MySQL  localhost:3306 ssl  JS > cluster.status();
{
    "clusterName": "myCluster", 
    "defaultReplicaSet": {
        "name": "default", 
        "primary": "cbdeaa365467:3306", 
        "ssl": "REQUIRED", 
        "status": "OK", 
        "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", 
        "topology": {
            "74f84ebea3d4:3306": {
                "address": "74f84ebea3d4:3306", 
                "memberRole": "SECONDARY", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.0.32"
            }, 
            "cbdeaa365467:3306": {
                "address": "cbdeaa365467:3306", 
                "memberRole": "PRIMARY", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.0.32"
            }, 
            "d0c473f84ec1:3306": {
                "address": "d0c473f84ec1:3306", 
                "memberRole": "SECONDARY", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "replicationLag": "applier_queue_applied", 
                "role": "HA", 
                "status": "ONLINE", 
                "version": "8.0.32"
            }
        }, 
        "topologyMode": "Single-Primary"
    }, 
    "groupInformationSourceMember": "cbdeaa365467:3306"
}

集群默认为单主模式,一个主节点,两个从节点。

宕机测试:

​ 当我们停掉一个主节点mysql-33061 时,集群依然对完提供正常服务,此时集群的主节点发生变化。当我们重启mysql-33061 后,新加入的节点作为从节点进入集群,宕机期间产生的数据也会在节点恢复后自动同步过来。

数据同步测试:

​ 当我们在主节点新建数据库、新增表、新增行数据、新建索引时,其他两个从库也会自动同步数据。但这些数据变动操作不可以在从库执行,从库只允许读取操作。

重启集群

当我们重启服务器之后,原先部署的集群元数据没有丢失,直接使用命令可以重启集群:

dba.rebootClusterFromCompleteOutage(); 

注意关闭防火墙

# 查看防火墙状态
[root@localhost ~]# firewall-cmd --state
# 关闭防火墙
[root@localhost ~]# systemctl stop firewalld.service
# 禁止防火墙开机重启
[root@localhost ~]# systemctl disable firewalld.service 
# SELECT VARIABLE_VALUE FROM performance_schema.global_variables WHERE VARIABLE_NAME='group_replication_group_name';

Admin API

可以使用MySQL Shell工具AdminAPI来检查集群的状态

MGR

​ MGR(MySQL Group Replication,MySQL组复制)是MySQL 5.7.17版本(2016 年 12 月)开始引入的,官方推出的高可用解决方案,以服务器插件的形式提供。MGR基于分布式 Paxos 协议,实现组复制,保证数据一致性。内置故障检测和自动选主功能,只要不是集群中的大多数节点都宕机,就可以继续正常工作。提供单主模式与多主模式,多主模式支持多点写入。

功能特性

1.MGR所有节点都有一份完整数据,发生故障时可以直接切换。

2.MGR提供了数据一致性保障,默认是最终一致性,可根据业务特征需要自行调整一致性级别。

3.支持在线添加、删除节点,节点管理更方便。

4.支持故障自动检测及自动切换,发生故障时能自动切换到新的主节点,再配合MySQL Router中间件,应用层无需干预或调整。

5.支持单主节点、多主节点两种模式,可根据架构或业务需要选择哪种方案。

两种模式

每个群组最多具有9个成员,9成员的情况下,最多允许4个成员出现故障。

单主模式

​ 单个MySQL实例作为数据写入的主节点,其它的节点用于热备。这个模式与传统的主从模式相似,当主节点故障时,自动在从节点中选择一个作为新的主节点。

多主模式

​ 多主模式,群组内所有的成员都可以进行数据写入、读取操作。

​ 使用多主模式时,由于数据的写入可以在所有的成员节点上进行,当在不同成员上对同一条记录同时进行更新时,就会产生冲突,此时群组复制会根据成员提交的先后次序(严格来讲是在群组复制的一致性校验阶段,取得校验成功的先后次序)进行判断,后提交事务的执行回滚处理。

冲突检测需要使用主键。

由于多主模式需要确保数据写入的一致性,所以在使用上有如下限制:

  • 仅支持innodb 存储引擎
  • 表必须有主键,或者非Null 的唯一键。
  • 开启Binlog,并且以行的方式记录;

集群部署

使用命令检查事,mysql shell 会逐一检查数据库的每张表是否使用InnoDB 存储引擎并且具有主键或非空唯一键。如果不符合要求,则报错。

Group Replication 要求表使用 InnoDB 并具有 PRIMARY KEY 或 PRIMARY KEY Equivalent(非空唯一键)。 不符合这些要求的表在与组复制一起使用时将可读但不可更新。 如果您的应用程序对这些表进行更新(INSERT、UPDATE 或 DELETE),请确保它们使用 InnoDB 存储引擎并具有 PRIMARY KEY 或 PRIMARY KEY Equivalent。

注:MySQL 8.0.30 新增了sql_generate_invisible_primary_key参数,参数值设置为 on 时,创建表时如果没有显式定义主键会自动添加一个不可见主键索引。

## sql_generate_invisible_primary_key=on 建的表
mysql> SHOW CREATE TABLE auto_1\G
*************************** 1. row ***************************
       Table: auto_1
Create Table: CREATE TABLE `auto_1` (
  `my_row_id` bigint unsigned NOT NULL AUTO_INCREMENT /*!80023 INVISIBLE */,
  `c1` varchar(50) DEFAULT NULL,
  `c2` int DEFAULT NULL,
  PRIMARY KEY (`my_row_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

该设计有以下特性:

  • 自动创建不可见主键,关键点在于”INVISIBLE” 关键字,那么 select * from xxx 出来的结果和之前是兼容的,不会多出来 my_row_id 列。
  • 如果表本身有主键,那么就不会自动创建主键。
  • 如果建表时没有显式指定主键,建表时请勿包含列名叫my_row_id的列,因为 GIPK 模式自动创建的不可见主键列固定就叫做my_row_id,会有冲突导致创建表失败。
  • GIPK 功能只在新建表时触发,如果表已经建了并且没有主键,请自行解决或重建表。

​ 他解决了卡住很多人上车 MGR 的一个硬性要求,表必须要有主键!实际上,没有主键在主从架构甚至单机架构也是不建议的,现在 GIPK 这个特性可以让那些懒惰的开发再也无法阻拦 MGR 的崛起。

脑裂处理

​ 使用MGR不会出现脑裂问题,MGR会检测网络分区。

​ 发生网络分区时,如果部分成员检测到大多数成员丢失,连接到这部分成员的数据更新处理将被挡住并等待,Select可以执行。假设一个包含五个节点的集群,S1 S2与其余三个成员失去联系,对于S1 S2来说他们已经丢失了群组中的大部分成员,因此不能够在它们上面执行数据更新处理(S3 S4 S5上面可以进行数据更新,当网络故障恢复后,S1 S2可以从S3 S4 S5上获取故障期间未更新的数据)。

数据一致性

​ MGR是通过日志的传播和施放来进行群组内所有成员的数据同步,因此,在某一时间点各个成员上数据是会出现不一致的情况(最终会一致)。在MySQL8.0.14之后,可以通过使用变量 group_replication_consistency 精确地控制每个节点上数据的一致性。

数据恢复

​ 当之前的故障节点恢复或者新节点加入集群时,第一件事就是进行数据恢复。

增量恢复

恢复时,可以采用增量恢复,增量恢复基于异步复制实现,只提取基于GTID 的丢失数据。

增量恢复可能会需要相当长的时间,并且当群组无法提供全部的binlog时,无法进行恢复,也就无法加入集群中。

所以当部分binlog被清理,而新节点还没有数据时,该怎么办呢,我们可以选择Clone 插件!(8.0.17开始提供)

MySQL Clone Plugin

MySQL Clone Plugin:

  • 执行数据库的物理快照
  • 通过网络将快照传递给服务器
  • 替换服务器的全部数据

使用克隆部署解决的问题:

  • 巨大的数据集
  • 不完整的二进制日志
  • 重新部署实例

MySQL Router

MySQL Router目前仍在成长中,如果你只需要负载均衡与主备,那么LVS性能更高,Haproxy也更成熟。

国内使用MySQL Router的案例非常少,所以MySQL Router我们还是只做了解,把精力放在比他更成熟的方案上。

​ MGR 保证数据库服务持续可用,但是当其中一个组成员变得不可用时,MGR 本身并不处理与客户端的容错关系,此时连接到该节点的客户端必须使用负载均衡器、路由器或某种形式的中间件。MySQL 官方团队提供 MySQL Router 来处理该问题 。

​ MySQL Router是一个轻量级的中间件,可以提供客户端负载均衡应用连接的故障转移。通过使用Router 和 Shell,用户可以利用MGR实现完整的数据库层的解决方案。如果您在使用MGR,请一定配合使用Router和Shell。

  • 读写分离
  • 读负载均衡
  • 故障自动转移

​ Router作为一个流量转发层,位于应用与MySQL服务器之间,其功能类似于LVS。应用不再直连MySQL Servers,而是与Router相连。根据Router的配置,将会把应用程序的读写请求转发给下游的MySQL Servers。

​ 考虑到Router独立部署可能引入“额外的部署成本”、“性能降级”、“连接数上限”等问题,通常建议基于“Agent”方式部署,即将Router与应用程序部署在同一台机器上。

​ Router中间件本身不会对请求“拆包”,所以无法在Router中间件上实现比如“SQL审计”、“隔离”、“限流”、“分库分表”等功能。

MySQL Operator

MySQL Operator for Kubernetes 是一个用于管理 Kubernetes 集群内的 MySQL InnoDB 集群设置的运营商。 它通过设置和维护管理整个生命周期,包括自动升级和备份。

​ MySQL Operator for Kubernetes 由 Oracle 的 MySQL 团队开发。

InnoDB Cluster Set

​ Mysql 8.0.27 版本开始提供InnoDB Cluster Set 功能。

​ MySQL InnoDB ClusterSet 为 InnoDB Cluster 部署提供容灾能力。InnoDB ClusterSet 使用专用的 ClusterSet 复制通道自动管理从主集群到副本集群的复制。如果主集群由于数据中心损坏或网络连接丢失而变得无法使用,用户可以激活副本集群以恢复服务的可用性。

限制

  • InnoDB ClusterSet 优先考虑可用性而不是一致性,以最大限度地提高容灾能力。如果用户不能容忍故障转移期间事务或数据的任何丢失,那么不要使用 InnoDB ClusterSet ,可以考虑使用单个 InnoDB Cluster。
  • InnoDB Cluster必须使用单主模式。
  • InnoDB ClusterSet 不会自动故障转移到副本集群。需要管理员确认后执行操作。
  • InnoDB ClusterSet 只支持异步复制,不能使用半同步复制。

README

作者:

​ 2023-03-04 银法王 第一次修订

参考:

MySQL 组复制文档

美团数据库高可用架构的演进与设想


MySQL数据库高可用方案InnoDBCluster
http://jackpot-lang.online/2022/11/08/数据库/MySQL高可用方案InnodbCluster/
作者
Jackpot
发布于
2022年11月8日
许可协议