Mysql Innodb cluster集群搭建

参考文章来源:
https://www.cnblogs.com/kevingrace/p/10466530.html

一.Innodb cluster集群介绍

  • MySQL的高可用架构无论是社区还是官方,一直在技术上进行探索,这么多年提出了多种解决方案,比如MMM, MHA, NDB Cluster, Galera Cluster, InnoDB Cluster, 腾讯的PhxSQL, MySQL Fabric ,aliSQL
  • MySQL官方在2017年4月推出了一套完整的、高可用的Mysql解决方案 - MySQL InnoDB Cluster, 即一组MySQL服务器可以配置为一个MySQL集群。在默认的单主节点模式下,集群服务器具有一个读写主节点和多个只读辅节点。辅助服务器是主服务器的副本。客户端应用程序通过MySQL Router连接到主服务程序。如果主服务连接失败,则次要的节点自动提升为主节点,MySQL Router请求到新的主节点。InnoDB Cluster不提供NDB Cluster支持。
  • 分布式MySQL之InnoDB和NDB
    分布式MySQL主要有InnoDB和NDB模式, NDB是基于集群的引擎-数据被自动切分并复制到数个机器上(数据节点), 适合于那些需要极高查询性能和高可用性的应用, 原来是为爱立信的电信应用设计的。 NDB提供了高达99.999%的可靠性,在读操作多的应用中表现优异。 对于有很多并发写操作的应用, 还是推荐用InnoDB。

========== NDB和InnoDB存储引擎之间的特性差异 ==========

InnoDB(MySQL 5.7)特性:
-  InnoDB版本:InnoDB 5.7.20;
-  NDB Cluster版本:不支持;
-  最大存储长度:64TB;
-  事物:所有标准事物类型;
-  多版本并发控制:支持;
-  数据压缩:支持;
-  大行支:VARBINARY、VARCHAR、BLOB;
-  同步支持:半同步、异步;
-  块读取:支持;
-  块写入:需要使用水平分区;
-  高可用性:高;
NDB 7.5/7.6特性:
-  InnoDB版本:InnoDB 5.7.20;
-  NDB Cluster版本: NDB 7.5.8/7.6.4;
-  最大存储长度:128TB;
-  事物:读提交;
-  多版本并发控制:不支持;
-  数据压缩支持:不支持;
-  大行支持:BLOB、 TEXT;
-  同步支持半:自动同步;
-  块读取:支持;
-  块写入:支持;
-  高可用性:非常高;
  • 本次部署采用InnoDB Cluster. 每台服务器实例都运行MySQL Group Replication(冗余复制机制,内置failover
  • MGR有两种模式,一种是Single-Primary,一种是Multi-Primary,即单主或者多主。
  • 注意:Multi-Primary模式中,所有的节点都是主节点,都可以同时被读写,看上去这似乎更好,但是因为多主的复杂性,在功能上如果设置了多主模式,则会有一些使用的限制,比如不支持Foreign Keys with Cascading Constraints。

MySQL InnoDB Cluster 集群特性, 有什么好处 (为什么使用它)

  • 集成易用
    MySQL InnoDB集群紧密集成了MySQL Servers with Group Replication,MySQL Router,和MySQL Shell,所以不必依赖于外部工具,脚本或其他部件。 另外它利用了现有的MySQL特性,如:InnoDB, GTIDs, binary logs, multi-threaded slave execution, multi-source replication and Performance Schema。可以在五分钟内利用MySQL Shell中的脚本化的管理API来创建及管理MySQL集群。
  • 使用组复制的mysql server HA
    组复制提供了内置的组成员管理、数据一致性保证、冲突检测和处理、节点故障检测和数据库故障转移相关操作的本地高可用性,无需人工干预或自定义工具。组复制同时实现了带自动选主的单主模式及任意更新的多主模式。通过使用一个强大的新的组通信系统,它提供了流行的Paxos算法的内部实现,来自动协调数据复制、一致性、membership。这提供了使MySQL数据库高度可用所需的所有内置机制。
  • 弹性
    通过组复制,一组服务器协调在一起形成一个组。组成员是动态的,服务器可以自愿或强制的地离开及随时加入。组将根据需要自动重新配置自己,并确保任何加入成员与组同步。这样就可以方便地在需要时快速地调整数据库的总容量。
  • 故障检测
    组复制实现了一个分布式故障检测器来查找并报告failed或不再参与组的服务器,组中剩余成员将重新配置。
  • 容错
    组复制基于流行的Paxos分布式算法来提供服务器之间的分布式协调。为了使一个小组继续发挥作用,它要求大多数成员在线,并就每一个变化达成协议。这允许MySQL数据库在发生故障时安全地继续操作,而无需人工干预,不存在数据丢失或数据损坏的风险。
  • 自愈
    如果一个服务器加入该组,它将自动将其状态与现有成员同步。如果服务器离开该组,例如它被取下来进行维护,剩下的服务器将看到它已离开,并将自动重新配置组。当服务器后重新加入组,它会自动重新与组同步。
  • 监测
    MySQL Enterprise Monitor 3.4及以后的版本全面支持组复制;监控每个节点的配置,健康,和性能。并且提供最佳实践建议和提醒,以及易于理解的可视化工具,允许您轻松地监控和管理您的组复制和InnoDB集群。
  • 通过MySQL Router为mysql客户机应用程序实现HA
    MySQL的路由器允许您轻松迁移您的独立的MySQL实例到本地分布式高可用集群而不影响现有的应用程序。新metadata_cache插件为Innodb 集群提供了透明的客户端连接路由、负载平衡和故障转移的能力。
  • 简单易用的MySQL shell
    MySQL Shell为所有MySQL相关的任务提供了一个直观、灵活、功能强大的接口。
    新的adminapi使得它很容易用一种自我描述的自然语言来创建,监控和管理包括MySQL Router在内的MySQL InnoDB集群,而不需要了解低层次的概念,配置选项,或其他复杂的方面。

二.工作原理

MySQL InnoDB集群提供了一个集成的,本地的,HA解决方案。Mysq Innodb Cluster是利用组复制的 pxos 协议,保障数据一致性,组复制支持单主模式和多主模式。

MySQL InnoDB集群由以下几部分组成:

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

各个组件的关系和工作流程如下:


图一

图二

图三

三.集群方案

我采用3台centos7.7 服务器来搭建MySQL Innodb cluster集群,分别是一主两从,如下部署:

ip地址                主机名          角色               安装软件
192.168.62.11        node-1      cluster节点1       Mysql5.7, mysql-shell, mysql-route
192.168.62.12        node-2      cluster节点2       Mysql5.7, mysql-shell, mysql-route
192.168.62.13        node-3      cluster节点3       Mysql5.7, mysql-shell

mysql,mysql shell 和mysql router的安装包如下:

  • mysql:mysql-5.7.29-linux-glibc2.12-x86_64.tar.gz
  • mysql-shell:mysql-shell-1.0.11-linux-glibc2.12-x86-64bit.tar.gz
  • mysql-router:mysql-router-2.1.6-linux-glibc2.12-x86-64bit.tar.gz

根据官方的文档 https://dev.mysql.com/doc/refman/8.0/en/mysql-innodb-cluster-userguide.html,mysql-shell需要python环境,至少在2.7版本以上,所以在安装使用前,需要提前部署好python环境。

查看python版本

python -V
Python 2.7.5

四.环境准备(所有服务器都要操作)

1.关闭防火墙,关闭方法因系统不同,所以不列出

2.关闭selinux(Centos),关闭方法因系统不同,所以不列出

3.配置每个节点的/etc/hosts主机映射, 方便通过节点的hostname进行连接
这一步很重要,否则可能会出现无法同步的情况,因为数据库需要根据member_host同步,如果不配置,默认就是localhost,这样时无法通信的!!!

vim /etc/hosts
...........
192.168.62.11    node-1
192.168.62.12    node-2
192.168.62.13    node-3

4.优化配置

[root@localhost ~]# cat>>/etc/sysctl.conf <<EOF
fs.aio-max-nr = 1048576
fs.file-max = 681574400
kernel.shmmax = 137438953472
kernel.shmmni = 4096
kernel.sem = 250 32000 100 200
net.ipv4.ip_local_port_range = 9000 65000
net.core.rmem_default = 262144
net.core.rmem_max = 4194304
net.core.wmem_default = 262144
net.core.wmem_max = 1048586
EOF
   
[root@localhost  ~]# sysctl -p   

#mysql服务需要打开15000个文件数,默认用户只有1024(通过ulimit -n 查询当前是多少),修改如下,其中cluster为我安装mysql使用的linux用户:
[root@localhost  ~]# cat>>/etc/security/limits.conf <<EOF
cluster soft nproc 65536
cluster hard nproc 65536
cluster soft nofile 65536
cluster hard nofile 65536
EOF

五.环境部署记录

5.1.在三个cluster节点安装和部署Mysql5.7及 mysql-shell。

三个节点同样操作,此处只列出第一个节点的安装配置记录

  1. 安装mysql5.7
    使用yum方式安装Mysql5.7,参考:
    https://www.cnblogs.com/kevingrace/p/8340690.html
    非root用户下安装mysql,参考:
    https://www.jianshu.com/p/d91e79b5146b

  2. 安装mysql-shell

#解压
tar -zvxf mysql-shell-1.0.11-linux-glibc2.12-x86-64bit.tar.gz
mv mysql-shell-1.0.11-linux-glibc2.12-x86-64bit mysql-shell
#在用户目录添加环境变量,编辑.bash_profile或者/etc/profile末尾添加
export PATH=$PATH:/home/cluster/mysql-shell/bin
#更新环境变量
source /etc/profile
#输入指令查询版本
mysqlsh --version       
mysqlsh   Ver 1.0.11 for Linux on x86_64 - for MySQL 5.7.20 (MySQL Community Server (GPL))
  1. 测试mysql-shell
[cluster@centos76 ~]$ mysqlsh
MySQL Shell 1.0.11

Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type '\help' or '\?' for help; '\quit' to exit.

Currently in JavaScript mode. Use \sql to switch to SQL mode and execute queries.
mysql-js> 
#连接本节点数据库
mysql-js> shell.connect('root@node-1:3305');
#回车,会提示输入密码
Please provide the password for 'root@node-1:3305': 
Creating a Session to 'root@node-1:3305'
Your MySQL connection id is 20
No default schema selected; type \use <schema> to set one.
mysql-js> 
#如上所示,表示连接成功

连接时可能会报如下错误:

Shell.connect: Host 'node-1' is not allowed to connect to this MySQL server (MySQL Error 1130)

需要在MySQL中,新建用户及授权,如下:

CREATE USER 'root'@'%' IDENTIFIED BY '123456';
grant all privileges on *.* to 'root'@'%' with grant option;
flush privileges; /*刷新权限*/
  1. 配置my.cnf
  • 配置前先备份
  • 修改不同机器的server_id、ip;3个cluster节点除了server_id、loose-group_replication_local_address 两个参数不一样外,其他保持一致。
  • loose-group_replication_group_name使用UUID形式,集群中机器使用同一个UUID;
  • loose-group_replication_single_primary_mode在单主模式中为ON,在多主模式中为OFF
  • 配置完成后, 将my.cnf文件拷贝到其他两个节点,并且依次重启三个节点的数据库,安装MGR插件,设置复制账号(所有MGR节点都要执行)
# For advice on how to change settings please see
# http://dev.mysql.com/doc/refman/5.7/en/server-configuration-defaults.html

[mysqld]
#
# Remove leading # and set to the amount of RAM for the most important data
# cache in MySQL. Start at 70% of total RAM for dedicated server, else 10%.
# innodb_buffer_pool_size = 128M
#
# Remove leading # to turn on a very important data integrity option: logging
# changes to the binary log between backups.
# log_bin
#
# Remove leading # to set options mainly useful for reporting servers.
# The server defaults are faster for transactions and fast SELECTs.
# Adjust sizes as needed, experiment to find the optimal values.
# join_buffer_size = 128M
# sort_buffer_size = 2M
# read_rnd_buffer_size = 2M
character-set-server = utf8
port = 3305
bind-address = node-1  #需提前在 /etc/hosts 增加 node-1记录

basedir = /home/cluster/mysql
datadir = /home/cluster/mysql/data
socket = /home/cluster/mysql/mysql.sock

log_error=/home/cluster/mysql/error.log
pid-file=/home/cluster/mysql/mysql.pid

# Disabling symbolic-links is recommended to prevent assorted security risks
#symbolic-links = 0

# Replication configuration parameters
server_id = 1
gtid_mode = ON
enforce_gtid_consistency = ON
log_bin = binlog
log_slave_updates = ON
binlog_format = ROW
binlog_checksum = NONE
master_info_repository=TABLE
relay_log_info_repository=TABLE
max_connections=200
max_allowed_packet=16M

#server必须为每个事务收集写集合,并使用XXHASH64哈希算法将其编码为散列
transaction_write_set_extraction=XXHASH64

#告知插件加入或创建组命名,UUID,集群中UUID一致
loose-group_replication_group_name="816dd734-5cfd-11ea-9ba1-fa163e443845"

#server启动时不自启组复制,为了避免每次启动自动引导具有相同名称的第二个组,所以设置为OFF。
loose-group_replication_start_on_boot=off

#告诉插件使用IP地址,端口24901用于接收组中其他成员转入连接
loose-group_replication_local_address= "node-1:3305"

#启动组server,种子server,加入组应该连接这些的ip和端口;其他server要加入组得由组成员同意
loose-group_replication_group_seeds="node-1:3305,node-2:3305,node-3:3305"
loose-group_replication_ip_whitelist="node-1,node-2,node-3"

loose-group_replication_bootstrap_group = off
loose-group_replication_allow_local_disjoint_gtids_join = ON

# 使用MGR的单主模式
loose-group_replication_single_primary_mode = on
loose-group_replication_enforce_update_everywhere_checks = off

# 禁止其他引擎创建库表
disabled_storage_engines = MyISAM,BLACKHOLE,FEDERATED,CSV,ARCHIVE

[client]
port=3305
default-character-set=utf8
socket=/home/cluster/mysql/mysql.sock

5.2.配置创建Innodb Cluster集群

  1. 持久化集群配置文件,会自动在指定的my.cnf文件中添加配置项 (需要三个节点同样操作)
mysqlsh
mysql-js> dba.configureLocalInstance('root@node-1:3305');
Please provide the password for 'root@node-1:3305': 
#输入密码
Detecting the configuration file...
Found configuration file at standard location: /etc/my.cnf
Do you want to modify this file? [Y|n]:  [Y|n]:
#由于配置时不是采用用默认的/etc/my.cnf文件,此处选n,然后指定配置文件路径
Default file not found at the standard locations.
Please specify the path to the MySQL configuration file:/home/cluster/mysql/my.cnf
Validating instance...

The instance 'node-2:3305' is valid for Cluster usage
You can now use it in an InnoDB Cluster.

{
    "status": "ok"
}
mysql-js> 

由上面的信息看出, status 为 ok 说明配置没问题了,检查my.cnf配置文件是否发生变化,可以查看实例状态

mysql-js > dba.checkInstanceConfiguration('root@node-1:3305');
  1. 在管理节点创建 cluster集群,这里我采用第一个节点作为管理节点创建,节点:node-1(只在管理节点操作创建,窗口一致保持打开,添加节点时也需要用到)
mysqlsh 
#连接
mysql-js > shell.connect('root@node-1:3305');
# 创建一个 cluster,命名为 'myCluster'
mysql-js > var cluster = dba.createCluster('myCluster');
A new InnoDB cluster will be created on instance 'root@node-1:3305'.

Creating InnoDB cluster 'myCluster' on 'root@node-1:3305'...
Adding Seed Instance...

Cluster successfully created. Use Cluster.addInstance() to add MySQL instances.
At least 3 instances are needed for the cluster to be able to withstand up to
one server failure.

mysql-js> 
# 创建成功后,查看cluster状态,可以看到master已经添加进cluster
mysql-js> cluster.status();
{
    "clusterName": "myCluster", 
    "defaultReplicaSet": {
        "name": "default", 
        "primary": "node-1:3305", 
        "status": "OK_NO_TOLERANCE", 
        "statusText": "Cluster is NOT tolerant to any failures.", 
        "topology": {
            "node-1:3305": {
                "address": "node-1:3305", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE"
            }
        }
    }
}
  1. 通过cluster状态查看,可以看到master已经添加进cluster,现在添加slave从节点 (在管理节点操作添加)
mysqlsh
#连接
mysql-js > shell.connect('root@node-1:3305');
# 添加实例
mysql-js> cluster.addInstance('root@node-2:3305');
# 如果 之前的管理终端关闭,重新打开,执行语句时会报错:
TypeError: Cannot read property 'status' of undefined
#这时需要先执行下面这条语句获取后,再添加节点
mysql-js> cluster=dba.getCluster();

# 先添加node-2节点
mysql-js> cluster.addInstance('root@node-2:3305');
A new instance will be added to the InnoDB cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.
#输入密码
Please provide the password for 'root@node-2:3305': 
Adding instance to the cluster ...

The instance 'root@node-2:3305' was successfully added to the cluster.
mysql-js> 

# 再添加node-3节点
mysql-js> cluster.addInstance('root@node-3:3305');
A new instance will be added to the InnoDB cluster. Depending on the amount of
data on the cluster this might take from a few seconds to several hours.

Please provide the password for 'root@node-3:3305': 
Adding instance to the cluster ...

The instance 'root@node-3:3305' was successfully added to the cluster.

mysql-js> 

#成功后查看集群状态
mysql-js> cluster.status();
{
    "clusterName": "myCluster", 
    "defaultReplicaSet": {
        "name": "default", 
        "primary": "node-1:3305", 
        "status": "OK", 
        "statusText": "Cluster is ONLINE and can tolerate up to ONE failure.", 
        "topology": {
            "node-1:3305": {
                "address": "node-1:3305", 
                "mode": "R/W", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE"
            }, 
            "node-2:3305": {
                "address": "node-2:3305", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE"
            }, 
            "node-3:3305": {
                "address": "node-3:3305", 
                "mode": "R/O", 
                "readReplicas": {}, 
                "role": "HA", 
                "status": "ONLINE"
            }
        }
    }
}mysql-js> 

通过上面cluster集群信息可知, node-1节点是主节点, 具有R/W读写权限, 其他两个节点是从节点, 具有R/O 只读权限。

5.3.在node-1和node-2分别安装mysql-route

1.mysql-route介绍


架构图
  • MySQL Router是处于应用client和dbserver之间的轻量级代理程序,它能检测,分析和转发查询到后端数据库实例,并把结果返回给client。是mysql-proxy的一个替代品。
  • Router实现读写分离,程序不是直接连接数据库IP,而是固定连接到mysql router。MySQL Router对前端应用是透明的。应用程序把MySQL Router当作是普通的mysql实例,把查询发给MySQL Router,而MySQL Router会把查询结果返回给前端的应用程序。
  • 从数据库服务器故障,业务可以正常运行。由MySQL Router来进行自动下线不可用服务器。程序配置不需要任何修改。
  • 主数据库故障,由MySQL Router来决定主从自动切换,业务可以正常访问。程序配置不需要做任何修改。
  1. 安装配置
# 解压
tar -xzvf mysql-router-2.1.6-linux-glibc2.12-x86-64bit.tar.gz 
mv mysql-router-2.1.6-linux-glibc2.12-x86-64bit mysql-router-init

修改用户配置文件vi .bash_profile,内容如下:

# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/.local/bin:$HOME/bin

export PATH

export PATH=$PATH:/home/cluster/mysql/bin
export PATH=$PATH:/home/cluster/mysql-shell/bin
export PATH=$PATH:/home/cluster/mysql-router-init/bin
source .bash_profile
mysqlprovision --version
mysqlprovision version 2.1.0
  1. 配置MySQL router
mysqlrouter --bootstrap root@node-1:3305 -d /home/cluster/mysql-router --conf-base-port 23306
# 选项说明参考 https://dev.mysql.com/doc/mysql-router/8.0/en/mysqlrouter.html
Please enter MySQL password for root: 

Bootstrapping MySQL Router instance at /home/cluster/mysql-router...
MySQL Router  has now been configured for the InnoDB cluster 'myCluster'.

The following connection information can be used to connect to the cluster.

Classic MySQL protocol connections to cluster 'myCluster':
- Read/Write Connections: localhost:23306  #读写端口
- Read/Only Connections: localhost:23307   #只读端口

X protocol connections to cluster 'myCluster':
- Read/Write Connections: localhost:23308
- Read/Only Connections: localhost:23309

会自动在/home/cluster/mysql-router目录下生成配置文件及启动脚本
进入 mysql-router 目录,配置并启动(可以修改配置文件, 也可以默认不修改)

#启动
/home/cluster/mysql-router/start.sh
PID 16819 written to /home/cluster/mysql-router/mysqlrouter.pid
#默认通过route连接mysql后, 23306端口连接后可以进行读写操作. 23307端口连接后只能进行只读操作.
$ netstat -nltp | grep 2330
tcp        0      0 0.0.0.0:23306           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:23307           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:23308           0.0.0.0:*               LISTEN      off (0.00/0/0)
tcp        0      0 0.0.0.0:23309           0.0.0.0:*               LISTEN      off (0.00/0/0)
这样就可以使用MySQL客户端连接router了.  下面验证下连接router:
  1. 管理MySQL Router
a) 管理节点本机mysql-shell连接:
[cluster@node-1 ~]$ mysqlsh --uri root@node-1:23306
 
b) 管理节点本机mysql连接:
[cluster@node-2 ~]$ mysql -u root -h node-1 -P 23306 -p
 
c) 远程客户机通过route连接mysql
[root@db-node01 ~]# mysql -u root -h 127.0.0.1 -P 23306 -p

此外, 还可以利用keepalived实现InnoDB Cluster的高可用, 即两台db-route管理节点, 通过VIP资源实现故障无感知切换.

推荐阅读更多精彩内容