前言 本文主要介绍 Deian 11 如何安装 Percona XtraDB Cluster 8.0 集群(三个物理节点),并基于 Haproxy + Keepalived (两个物理节点)实现双机热备方案。
系列教程 官方文档 准备工作 部署架构
部署规划 软件 版本 描述 Haproxy 1.5.18 Keepalived 1.3.5 Percona XtraDB Cluster (PXC) 8.0
节点名称 主机名 IP 系统 说明 PXC 节点一 pxc-node-1 192.168.1.188 Debian 11 (Bullseye) Percona XtraDB Cluster (PXC) PXC 节点二 pxc-node-2 192.168.1.193 Debian 11 (Bullseye) Percona XtraDB Cluster (PXC) PXC 节点三 pxc-node-3 192.168.1.223 Debian 11 (Bullseye) Percona XtraDB Cluster (PXC) Haproxy 节点一 haproxy-node-1 192.168.1.235 Debian 11 (Bullseye) Haproxy + Keepalived Haproxy 节点二 haproxy-node-2 192.168.1.239 Debian 11 (Bullseye) Haproxy + Keepalived
提示
Percona XtraDB Cluster 要求最小的集群大小是 3 个节点。 建议尽可能地控制 PXC 集群的规模,节点越多,数据同步速度越慢。 PXC 存在硬件配置短板限制,即整个集群的写吞吐量受最弱节点的限制。因此所有 PXC 节点的硬件配置要一致,否则如果一个节点变慢,整个集群会跟着变慢。 系统初始化 配置 Host 在每个节点服务器上编辑 /etc/hosts
文件,加入以下内容:
1 2 3 4 5 192.168.1.188 pxc-node-1 192.168.1.193 pxc-node-2 192.168.1.223 pxc-node-3 192.168.1.235 haproxy-node-1 192.168.1.239 haproxy-node-2
设置主机名 在每个节点服务器上设置主机名。若由于其他限制导致不允许更改主机名,则可以跳过以下步骤。
1 2 3 4 5 $ hostnamectl $ sudo hostnamectl set-hostname xx-xxxx
配置防火墙 在配置系统防火墙之前,在每个服务器上分别安装 UFW 防火墙工具。
1 2 3 4 5 6 7 8 9 10 11 sudo apt install ufw sudo ufw allow OpenSSH sudo ufw enable sudo ufw status
PXC 集群的防火墙 在本节中,将在每个 PXC 集群节点服务器上使用 UFW 配置防火墙,使 PXC 集群节点之间可以互相通信。
PXC 集群默认会使用到以下端口
基于 UFW 配置防火墙
1 2 3 4 5 sudo ufw allow from 192.168.1.0/24 proto tcp to any port 3306 sudo ufw allow from 192.168.1.0/24 proto tcp to any port 4444 sudo ufw allow from 192.168.1.0/24 proto tcp to any port 4567 sudo ufw allow from 192.168.1.0/24 proto tcp to any port 4568
1 2 3 4 5 6 7 8 9 10 Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere 3306/tcp ALLOW 192.168.1.0/24 4444/tcp ALLOW 192.168.1.0/24 4567/tcp ALLOW 192.168.1.0/24 4568/tcp ALLOW 192.168.1.0/24 OpenSSH (v6) ALLOW Anywhere (v6)
Haproxy 的防火墙 在本节中,将在每个 Haproxy 节点服务器上使用 UFW 配置防火墙,使外部可以正常访问 Haproxy。
Haproxy 默认会使用到以下端口
3306
:Haproxy 代理 MySQL 的端口8888
:Haproxy 监控页面的 Web 端口基于 UFW 配置防火墙
1 2 3 sudo ufw allow from 192.168.1.0/24 proto tcp to any port 3306 sudo ufw allow from 192.168.1.0/24 proto tcp to any port 8888
1 2 3 4 5 6 7 8 Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere 3306/tcp ALLOW 192.168.1.0/24 8888/tcp ALLOW 192.168.1.0/24 OpenSSH (v6) ALLOW Anywhere (v6)
Keepalived 的防火墙 在本节中,将在每个 Keepalived 节点服务器上使用 UFW 配置防火墙,使 Keepalived 节点之间可以互相进行心跳通信。
Keepalived 心跳通信
Keepalived 使用 VRRP 协议进行通信(协议号码为 112
) Keepalived 用于心跳通信的默认 VRRP 广播地址是 224.0.0.18
如果 Keepalived 节点之间不能正常进行心跳通信,就会发生脑裂现象,即多个 Keepalived 节点会同时抢占到虚拟 IP 基于 UFW 配置防火墙
1 2 ufw allow in on enp0s3 from 192.168.1.0/24 to 224.0.0.18;
1 2 3 4 5 6 7 8 9 Status: active To Action From -- ------ ---- OpenSSH ALLOW Anywhere 3306/tcp ALLOW 192.168.1.0/24 8888/tcp ALLOW 192.168.1.0/24 224.0.0.18 on enp0s3 ALLOW 192.168.1.0/24 OpenSSH (v6) ALLOW Anywhere (v6)
关闭 SeLinux 在本节中,将在每个节点服务器上永久关闭 SeLinux,保证 PXC 集群节点可以互相通信。值得一提的是,如果系统没有安装 SeLinux,则可以跳过以下步骤。
1 2 sudo vi /etc/selinux/config
1 2 3 4 5 6 7 8 9 10 11 # This file controls the state of SELinux on the system. # SELINUX= can take one of these three values: # enforcing - SELinux security policy is enforced. # permissive - SELinux prints warnings instead of enforcing. # disabled - No SELinux policy is loaded. SELINUX=disabled # SELINUXTYPE= can take one of three two values: # targeted - Targeted processes are protected, # minimum - Modification of targeted policy. Only selected processes are protected. # mls - Multi Level Security protection. SELINUXTYPE=targeted
系统性能优化 在本节中,将在每个节点服务器上更改系统的最大打开文件描述符数,且更改永久生效。
提示
关于更改最大打开文件描述符数的详细教程,可以看 这里 。 PXC 集群无需配置 Percona Server 数据库的最大打开文件描述符数,因为在 Percona Server 服务的配置文件 /lib/systemd/system/mysql.service
和 /lib/systemd/system/mysql@.service
中,默认都已经配置了 LimitNOFILE=16364
。 1 2 3 4 5 6 7 8 9 10 11 12 13 sudo vim /etc/security/limits.conf * soft nofile 1048576 * hard nofile 1048576 sudo vim /etc/sysctl.conf fs.file-max = 1048576 sudo reboot
1 2 3 ulimit -nsudo sysctl fs.file-max
卸载已安装软件 检查每个节点服务器上的 Debian 系统是否已经安装过 MySQL、MariaDB、Percona Server 数据库,如果安装过请先卸载掉,然后再安装 PXC 集群。
检查交换分区的大小 检查每个节点服务器上的交换分区大小,如果 Debian 系统没有交换分区,则建议手动创建并挂载,详细教程请看 这里 。
交换分区大小配置规则如果 RAM 介于 2 GB 和 8 GB 之间,则 SWAP 为 RAM 大小的 2 倍。 如果 RAM 在 8 GB 到 32 GB 之间,则 SWAP 为 RAM 大小的 1.5 倍。 如果 RAM 大于 32 GB,则 SWAP 为 32 GB。 PXC 集群安装 在本节中,将在每个节点服务器上,基于 Debian 11 添加并设置 Percona XtraDB Cluster 存储库,然后安装 Percona XtraDB Cluster 软件包。此外,在安装过程中,系统会提示设置 MySQL 的 root
账号的密码,并为 Percona XtraDB Cluster 设置默认身份验证插件。最后,将通过配置的 root
用户和密码登录数据库,验证 Percona XtraDB Cluster 的安装。值得一提的,PXC 集群的部署规划如下表所示:
节点名称 主机名 IP 系统 说明 PXC 节点一 pxc-node-1 192.168.1.188 Debian 11 (Bullseye) Percona XtraDB Cluster (PXC) PXC 节点二 pxc-node-2 192.168.1.193 Debian 11 (Bullseye) Percona XtraDB Cluster (PXC) PXC 节点三 pxc-node-3 192.168.1.223 Debian 11 (Bullseye) Percona XtraDB Cluster (PXC)
安装步骤 1 2 sudo apt install -y wget gnupg2 lsb-release curl
安装 Percona XtraDB Cluster 的存储库包 1 2 3 4 5 6 7 8 wget -q https://repo.percona.com/apt/percona-release_latest.generic_all.deb sudo dpkg -i percona-release_latest.generic_all.deb sudo apt-get update
启用 Percona XtraDB Cluster 8.0 (相当于 MySQL 8.0)的存储库 1 sudo percona-release setup pxc80
安装 Percona XtraDB Cluster 软件包 1 2 3 4 5 sudo apt install -y percona-xtradb-cluster sudo apt install -y -o Acquire::http::proxy="socks5h://127.0.0.1:1080/" percona-xtradb-cluster
1 2 3 4 5 sudo systemctl start mysql sudo systemctl status mysql
安装测试 在每个节点服务器上执行以下命令,然后输入 root
账号的密码,若能成功登录 MySQL,则说明数据库正常运行。
1 2 sudo mysql -h localhost -u root -p
PXC 集群配置 特别注意,在开始配置 Percona XtraDB Cluster 集群之前,必须确保所有节点上的 MySQL 服务器都已停止运行。
1 2 3 4 5 sudo systemctl stop mysql@bootstrap.service sudo systemctl status mysql@bootstrap.service
1 2 3 4 5 sudo systemctl stop mysql sudo systemctl status mysql
拷贝 SSL/TLS 证书 Percona XtraDB Cluster 有两种流量加密:客户端 / 服务器连接和复制流量。在最新的 Percona XtraDB Cluster 8.0 上,默认情况下会启用所有复制流量的加密以增强安全性。因此在创建和设置 Percona XtraDB Cluster 时,所有服务器都必须具有相同的 CA 和 Server 证书,即必须将默认的 CA 和 Server 证书从 pxc-node-1
节点拷贝到 pxc-node-2
节点和 pxc-node-3
节点。
在 PXC 集群安装的过程中,SSL/TLS 证书已经在数据目录 /var/lib/mysql
下自动生成了,包括 Client、Server、CA 三种类型的证书 1 2 ls /var/lib/mysql/*.pem
1 2 3 4 5 6 7 8 /var/lib/mysql/ca.pem /var/lib/mysql/ca-key.pem /var/lib/mysql/public_key.pem /var/lib/mysql/private_key.pem /var/lib/mysql/client-key.pem /var/lib/mysql/client-cert.pem /var/lib/mysql/server-key.pem /var/lib/mysql/server-cert.pem
在 pxc-node-1
节点上,拷贝 CA 和 Server 证书到 pxc-node-2
节点和 pxc-node-3
节点。 1 2 3 4 5 6 cd /var/lib/mysqlscp server-key.pem server-cert.pem ca.pem root@pxc-node-2:/var/lib/mysql scp server-key.pem server-cert.pem ca.pem root@pxc-node-3:/var/lib/mysql
在第一个节点上初始化集群 在本节中,将在第一个节点服务器 pxc-node-1
上初始化 Percona XtraDB Cluster 集群(即引导 PXC 集群启动)。请确保以下步骤都是在节点一服务器上执行。
编辑节点一的 MySQL 配置文件,添加以下配置内容 1 2 3 4 5 sudo cp /etc/mysql/mysql.conf.d/mysqld.cnf /etc/mysql/mysql.conf.d/mysqld.cnf.bak sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 [client] socket=/var/run/mysqld/mysqld.sock [mysqld] server-id=1 datadir=/var/lib/mysql socket=/var/run/mysqld/mysqld.sock log-error=/var/log/mysql/error.log pid-file=/var/run/mysqld/mysqld.pid ######## wsrep ############### # Path to Galera library wsrep_provider=/usr/lib/libgalera_smm.so # Cluster connection URL contains the IPs of pxc01, pxc02, and pxc03 wsrep_cluster_address=gcomm://192.168.1.188,192.168.1.193,192.168.1.223 # In order for Galera to work correctly binlog format should be ROW binlog_format=ROW # Slave thread to use wsrep_slave_threads=4 # Using the MyISAM storage engine is not recommended. default_storage_engine=InnoDB # This InnoDB autoincrement locking mode is a requirement for Galera innodb_autoinc_lock_mode=2 # Node 1 address wsrep_node_address=192.168.1.188 # SST method wsrep_sst_method=xtrabackup-v2 # Cluster name wsrep_cluster_name=pxc_cluster # If is not specified, then system hostname will be used wsrep_node_name=pxc-cluster-node-1 # PXC Strict Mode allowed values: DISABLED,PERMISSIVE,ENFORCING,MASTER pxc_strict_mode=ENFORCING wsrep_provider_options="socket.ssl_key=server-key.pem;socket.ssl_cert=server-cert.pem;socket.ssl_ca=ca.pem" [sst] encrypt=4 ssl-key=server-key.pem ssl-ca=ca.pem
1 2 3 4 5 6 7 8 9 10 11 核心参数说明: - `wsrep_cluster_name`:集群的名称 - `server-id`:MySQL 实例的唯一标识符(每个节点都不一样) - `wsrep_node_name`:当前节点服务器的名称(每个节点都不一样),如果不指定默认使用主机名 - `wsrep_node_address`:当前节点服务器的 IP 地址(每个节点都不一样) - `wsrep_cluster_address`:所有节点服务器的 IP 地址 其他参数说明: - `default_storage_engine=InnoDB`:指定默认的存储引擎为 InnoDB - `innodb_autoinc_lock_mode=2`:该模式下所有 INSERT SQL 都不会有表级 AUTO-INC 锁,即多个语句可以同时执行 - `pxc_strict_mode=ENFORCING`:严厉模式,`ENFORCING` 表示会阻止用户执行 Percona XtraDB Cluster 所不支持的功能
1 2 systemctl start mysql@bootstrap.service
特别注意
mysql@bootstrap
是一个用于运行 Percona XtraDB Cluster 的 Systemd 服务,这与普通的 mysql
服务有本质的区别,主要用于 PXC 集群的初始化(即引导 PXC 集群启动)。使用 Percona XtraDB Cluster 构建 MySQL 服务器时,第一个节点必须使用 mysql@bootstrap
服务进行管理,包括启动、关闭、重启、查看状态等操作(如下所示),而其他节点则可以直接使用普通的 mysql
服务进行管理。 启动第一个节点服务器:systemctl start mysql@bootstrap.service
。 关闭第一个节点服务器:systemctl stop mysql@bootstrap.service
。 重启第一个节点服务器:systemctl restart mysql@bootstrap.service
。 查看第一个节点服务器的状态:systemctl status mysql@bootstrap.service
。 1 2 sudo mysql -h localhost -u root -p
1 2 show status like 'wsrep%' ;
提示
当 PXC 集群初始化成功后,应该可以看到下述的状态信息。wsrep_cluster_size
的值是 1
,这意味着 Percona XtraDB Cluster 是用一台服务器初始化的。wsrep_incoming_address
的值是节点一服务器的 IP 地址。最后,节点处于 Synced
状态,这意味着它已完全连接并准备好进行写集复制。
将第二个节点添加到集群中 1 2 3 4 5 sudo cp /etc/mysql/mysql.conf.d/mysqld.cnf /etc/mysql/mysql.conf.d/mysqld.cnf.bak sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 [client] socket=/var/run/mysqld/mysqld.sock [mysqld] server-id=2 datadir=/var/lib/mysql socket=/var/run/mysqld/mysqld.sock log-error=/var/log/mysql/error.log pid-file=/var/run/mysqld/mysqld.pid ######## wsrep ############### # Path to Galera library wsrep_provider=/usr/lib/libgalera_smm.so # Cluster connection URL contains the IPs of pxc01, pxc02, and pxc03 wsrep_cluster_address=gcomm://192.168.1.188,192.168.1.193,192.168.1.223 # In order for Galera to work correctly binlog format should be ROW binlog_format=ROW # Slave thread to use wsrep_slave_threads=4 # Using the MyISAM storage engine is not recommended. default_storage_engine=InnoDB # This InnoDB autoincrement locking mode is a requirement for Galera innodb_autoinc_lock_mode=2 # Node 2 address wsrep_node_address=192.168.1.193 # SST method wsrep_sst_method=xtrabackup-v2 # Cluster name wsrep_cluster_name=pxc_cluster # If is not specified, then system hostname will be used wsrep_node_name=pxc-cluster-node-2 # PXC Strict Mode allowed values: DISABLED,PERMISSIVE,ENFORCING,MASTER pxc_strict_mode=ENFORCING wsrep_provider_options="socket.ssl_key=server-key.pem;socket.ssl_cert=server-cert.pem;socket.ssl_ca=ca.pem" [sst] encrypt=4 ssl-key=server-key.pem ssl-ca=ca.pem
特别注意
server-id
、wsrep_node_address
、wsrep_node_name
这三个参数必须跟其他节点一、节点三不一样。
验证节点二是否成功添加到集群,在节点二服务器上执行以下命令 1 2 3 4 5 sudo systemctl start mysql sudo systemctl status mysql
1 2 sudo mysql -h localhost -u root -p
1 2 show status like 'wsrep%' ;
将第三个节点添加到集群中 1 2 3 4 5 sudo cp /etc/mysql/mysql.conf.d/mysqld.cnf /etc/mysql/mysql.conf.d/mysqld.cnf.bak sudo vi /etc/mysql/mysql.conf.d/mysqld.cnf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 [client] socket=/var/run/mysqld/mysqld.sock [mysqld] server-id=3 datadir=/var/lib/mysql socket=/var/run/mysqld/mysqld.sock log-error=/var/log/mysql/error.log pid-file=/var/run/mysqld/mysqld.pid ######## wsrep ############### # Path to Galera library wsrep_provider=/usr/lib/libgalera_smm.so # Cluster connection URL contains the IPs of pxc01, pxc02, and pxc03 wsrep_cluster_address=gcomm://192.168.1.188,192.168.1.193,192.168.1.223 # In order for Galera to work correctly binlog format should be ROW binlog_format=ROW # Slave thread to use wsrep_slave_threads=4 # Using the MyISAM storage engine is not recommended. default_storage_engine=InnoDB # This InnoDB autoincrement locking mode is a requirement for Galera innodb_autoinc_lock_mode=2 # Node 3 address wsrep_node_address=192.168.1.223 # SST method wsrep_sst_method=xtrabackup-v2 # Cluster name wsrep_cluster_name=pxc_cluster # If is not specified, then system hostname will be used wsrep_node_name=pxc-cluster-node-3 # PXC Strict Mode allowed values: DISABLED,PERMISSIVE,ENFORCING,MASTER pxc_strict_mode=ENFORCING wsrep_provider_options="socket.ssl_key=server-key.pem;socket.ssl_cert=server-cert.pem;socket.ssl_ca=ca.pem" [sst] encrypt=4 ssl-key=server-key.pem ssl-ca=ca.pem
特别注意
server-id
、wsrep_node_address
、wsrep_node_name
这三个参数必须跟其他节点一、节点二不一样。
验证节点三是否成功添加到集群,在节点三服务器上执行以下命令 1 2 3 4 5 sudo systemctl start mysql sudo systemctl status mysql
1 2 sudo mysql -h localhost -u root -p
1 2 show status like 'wsrep%' ;
PXC 集群测试 在本节中,将对 PXC 集群的复制进行测试,包括创建数据库和表、插入数据。
创建数据库 登录节点二的 MySQL 服务器,创建 percona
数据库 1 2 CREATE DATABASE `percona` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
登录节点一或者节点三的 MySQL 服务器,观察是否同步创建了相同的数据库 创建数据库表 登录节点三的 MySQL 服务器,创建 example
数据库表 1 2 3 4 5 USE `percona`; CREATE TABLE example (node_id INT PRIMARY KEY, node_name VARCHAR(30));
登录节点一的 MySQL 服务器,往 example
数据库表插入数据 1 2 3 4 5 6 7 USE `percona`; INSERT INTO percona.example VALUES (1, 'pxc01' ); INSERT INTO percona.example VALUES (2, 'pxc02' ); INSERT INTO percona.example VALUES (3, 'pxc03' );
登录节点二的 MySQL 服务器,查询 example
表的数据 1 2 3 4 5 USE `percona`; SELECT * FROM percona.example;
PXC 集群管理 创建用户操作 登录任意一个 PXC 集群节点的数据库,执行以下命令创建新用户,并授权用户可以远程访问指定的数据库。
1 2 sudo mysql -h localhost -u root -p
1 2 3 4 5 6 7 8 9 10 11 CREATE USER 'uatOption' @'%' IDENTIFIED WITH mysql_native_password BY 'Pxc_User_123@456' ; GRANT ALL PRIVILEGES ON percona.* TO 'uatOption' @'%' ; FLUSH PRIVILEGES; SELECT host, user, plugin, authentication_string FROM mysql.user;
提示
这里为了兼容 MySQL 5 的认证方式,建议在创建数据库用户时,指定加密规则为 mysql_native_password
。值得一提的是,MySQL 8 默认使用的加密规则是 caching_sha2_password
。
集群各指标检查 在检测 PXC 集群各指标之前,先登录任意一个 PXC 集群节点的数据库,然后再执行其他操作。
1 2 sudo mysql -h localhost -u root -p
集群完整性检查 1 show global status where variable_name in ('wsrep_cluster_state_uuid' ,'wsrep_cluster_conf_id' ,'wsrep_cluster_size' ,'wsrep_cluster_status' );
1 2 3 4 5 6 7 8 9 +--------------------------+--------------------------------------+ | Variable_name | Value | +--------------------------+--------------------------------------+ | wsrep_cluster_conf_id | 7 | | wsrep_cluster_size | 3 | | wsrep_cluster_state_uuid | e4bb8bf0-73d5-11ee-b279-3a5ebcc11ef7 | | wsrep_cluster_status | Primary | +--------------------------+--------------------------------------+ 4 rows in set (0.00 sec)
特别注意,在正常情况下以下指标值,在所有节点应该都是一致的。
指标 说明 wsrep_cluster_state_uuid
在集群所有节点中该值应该是相同的,若有不同值,说明该节点没有连入集群。 wsrep_cluster_conf_id
在集群所有节点中该值应该是相同的,若有不同值,说明该节点被临时 分区
了,当节点之间网络连接恢复后,该值应该恢复成一致。 wsrep_cluster_size
如果与集群中的节点数一致,说明所有节点已经连接。 wsrep_cluster_status
集群状态,若不为 Primary
,说明出现 分区
或是 split-brain
状况。
集群节点状态检查 1 show global status where variable_name in ('wsrep_ready' ,'wsrep_connected' ,'wsrep_local_state_comment' );
1 2 3 4 5 6 7 +---------------------------+--------+ | Variable_name | Value | +---------------------------+--------+ | wsrep_connected | ON | | wsrep_local_state_comment | Synced | | wsrep_ready | ON | +---------------------------+--------+
特别注意,在正常情况下以下指标值,在所有节点应该都是一致的。
指标 说明 wsrep_ready
该值为 ON
,则说明可以接受 SQL 负载;如果为 OFF
,则需要检查 wsrep_connected
。 wsrep_connected
如果该值为 OFF
,且 wsrep_ready
的值也为 OFF
,则说明该节点没有连入集群,可能是 wsrep_cluster_address
或 wsrep_cluster_name
等配置错误造成的,具体需要排查 MySQL 的错误日志。 wsrep_local_state_comment
若 wsrep_connected
为 ON
,但 wsrep_ready
为 OFF
,则可以从该项查找错误原因。
集群健康信息检查 1 show global status where variable_name in ('wsrep_flow_control_paused' ,'wsrep_cert_deps_distance' ,'wsrep_flow_control_sent' ,'wsrep_local_recv_queue_avg' );
1 2 3 4 5 6 7 8 9 +----------------------------+-------+ | Variable_name | Value | +----------------------------+-------+ | wsrep_cert_deps_distance | 1 | | wsrep_flow_control_paused | 0 | | wsrep_flow_control_sent | 0 | | wsrep_local_recv_queue_avg | 0 | +----------------------------+-------+ 4 rows in set (0.00 sec)
指标 说明 wsrep_flow_control_paused
表示数据复制停止了多长时间(即因 Slave 延迟而慢的程度,取值范围为 0 ~ 1,越靠近 0 越好,值为 1 表示数据复制完全停止(停止广播),可优化 wsrep_slave_threads
的值来改善)。 wsrep_cert_deps_distance
表示有多少事务可以并行应用处理,wsrep_slave_threads
设置的值不应该高出该值太多。 wsrep_flow_control_sent
表示该节点已经停止复制了多少次。 wsrep_local_recv_queue_avg
表示 Slave 事务队列的平均长度,可作为 Slave 瓶颈的预兆。
Haproxy + Keepalived 双机热备 在本节中,将使用 Haproxy + Keepalived 实现双机热备方案,其中 Haproxy 负责将请求转发给 PXC 集群各个节点。值得一提的,Haproxy 与 Keepalived 的部署规划如下表所示:
节点名称 主机名 IP 系统 说明 Haproxy 节点一 haproxy-node-1 192.168.1.235 Debian 11 (Bullseye) Haproxy + Keepalived Haproxy 节点二 haproxy-node-2 192.168.1.239 Debian 11 (Bullseye) Haproxy + Keepalived
双机热备架构
创建数据库用户 在本节中,将创建 MySQL 用户,Haproxy 后续会使用这个用户对 PXC 集群节点进行心跳检测。
1 2 sudo mysql -h localhost -u root -p
创建数据库用户 haproxy
,不指定密码和权限,只允许远程访问 1 2 3 4 5 6 7 8 CREATE USER 'haproxy' @'%' IDENTIFIED WITH mysql_native_password; FLUSH PRIVILEGES; SELECT host, user, plugin, authentication_string FROM mysql.user;
特别注意
MySQL 8.0 默认使用的加密规则是 caching_sha2_password
,为了让 Haproxy 可以对 PXC 集群节点进行心跳检测,在创建数据库用户时,必须指定加密规则为 mysql_native_password
,否则 Haproxy 无法正常检测 PXC 集群节点的运行状态。
Haproxy 安装 在本节中,将在两个单独的服务器节点上分别安装 Haproxy 服务(两个节点的安装步骤和配置内容基本一致),实现对 PXC 集群的负载均衡。
1 2 3 4 5 sudo apt-get install -y haproxy sudo systemctl enable haproxy
1 2 3 4 5 sudo cp /etc/haproxy/haproxy.cfg /etc/haproxy/haproxy.cfg.bak sudo vim /etc/haproxy/haproxy.cfg
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 global # 工作目录 # chroot /usr/local/etc/haproxy # 日志文件,使用rsyslog服务中local5日志设备(/var/log/local5),等级info log 127.0.0.1 local5 info # 以守护进程运行 daemon defaults log global mode http # 日志格式 option httplog # 日志中不记录负载均衡的心跳检测记录 option dontlognull # 连接超时(毫秒) timeout connect 5000 # 客户端超时(毫秒) timeout client 50000 # 服务器超时(毫秒) timeout server 50000 # 监控界面 listen admin_stats # 监控界面的访问的IP和端口 bind 0.0.0.0:8888 # 访问协议 mode http # URI相对地址 stats uri /dbs # 统计报告格式 stats realm Global\ statistics # 登陆账户信息 stats auth admin:admin # 数据库负载均衡 listen proxy-pxc # 访问的IP和端口 bind 0.0.0.0:3306 # 网络协议 mode tcp # 负载均衡算法(轮询算法) # 轮询算法:roundrobin # 权重算法:static-rr # 最少连接算法:leastconn # 请求源IP算法:source balance roundrobin # 日志格式 option tcplog # Haproxy使用MySQL的haproxy账户对数据库进行心跳检测 option mysql-check user haproxy server PXC_Node_1 192.168.1.188:3306 check weight 1 maxconn 2000 server PXC_Node_2 192.168.1.193:3306 check weight 1 maxconn 2000 server PXC_Node_3 192.168.1.223:3306 check weight 1 maxconn 2000 # 使用Keepalived检测死链 option tcpka
Haproxy 配置说明
bind 0.0.0.0:8888
: 8888
端口用于 Haproxy 提供监控界面的 Web 服务。bind 0.0.0.0:3306
: 3306
端口用于 Haproxy 转发请求给 PXC 集群节点。option mysql-check user haproxy
: 指定 Haproxy 使用 MySQL 的 haproxy
账户对 PXC 集群节点进行心跳检测。1 2 3 4 5 sudo systemctl restart haproxy sudo systemctl status haproxy
使用浏览器访问不同节点的 Haproxy 监控页面(如下图所示),登录用户名是 admin
,登录密码是 admin
。如果可以正常访问 Haproxy 的监控界面,则说明 Haproxy 成功部署。
Haproxy 节点一的监控页面: http://192.168.1.235:8888/dbs
Haproxy 节点二的监控页面: http://192.168.1.239:8888/dbs
Keepalived 安装 在本节中,将在两个单独的 Haproxy 服务器节点上分别安装 Keepalived 服务,实现 Haproxy + Keepalived 的双机热备方案。
提示
Keepalived 安装完成后,默认会将配置模板文件存放在 /usr/share/doc/keepalived/samples/
目录下。
Haproxy 节点一安装 Keepalived 1 2 3 4 5 sudo apt-get install -y keepalived sudo systemctl enable keepalived
1 2 sudo vim /etc/keepalived/keepalived.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 vrrp_instance VI_1 { state MASTER # 必填,Keepalived 的身份(MASTER 是主服务器,BACKUP 是备服务器) interface enp0s3 # 必填,系统的网卡设备,虚拟 IP 所在 virtual_router_id 51 # 必填,虚拟路由标识,取值在0-255之间,用来区分多个Instance的VRRP组播,同一网段内ID不能重复,主备机器的该值必须为一样 priority 100 # 必填,用来选举Master的,MASTER 权重要高于 BACKUP,数字越大优先级越高,该项取值范围是1-255(在此范围之外会被识别成默认值100) advert_int 1 # 必填,MASTER 和 BACKUP 节点同步检查的时间间隔(单位为秒),主备之间必须一致,可以认为是健康检查的时间间隔 authentication { # 必填,主备服务器的验证方式,主备之间必须使用相同的密码才能正常通信 auth_type PASS # 必填,主备服务器的认证方式,其中有两种方式PASS和HA(IPSEC),推荐使用PASS(密码只识别前8位),主备之间必须使用相同的密码才能正常通信 auth_pass 123456 } virtual_ipaddress { # 必填,虚拟 IP,可以设置多个虚拟 IP 地址,每行一个 192.168.1.173 } }
1 2 3 4 5 sudo systemctl start keepalived sudo systemctl status keepalived
Haproxy 节点二安装 Keepalived 1 2 3 4 5 sudo apt-get install -y keepalived sudo systemctl enable keepalived
1 2 sudo vim /etc/keepalived/keepalived.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 vrrp_instance VI_1 { state BACKUP # 必填,Keepalived 的身份(MASTER 是主服务器,BACKUP 是备服务器) interface enp0s3 # 必填,系统的网卡设备,虚拟 IP 所在 virtual_router_id 51 # 必填,虚拟路由标识,取值在0-255之间,用来区分多个Instance的VRRP组播,同一网段内ID不能重复,主备机器的该值必须为一样 priority 90 # 必填,用来选举Master的,MASTER 权重要高于 BACKUP,数字越大优先级越高,该项取值范围是1-255(在此范围之外会被识别成默认值100) advert_int 1 # 必填,MASTER 和 BACKUP 节点同步检查的时间间隔(单位为秒),主备之间必须一致,可以认为是健康检查的时间间隔 authentication { # 必填,主备服务器的验证方式,主备之间必须使用相同的密码才能正常通信 auth_type PASS # 必填,主备服务器的认证方式,其中有两种方式PASS和HA(IPSEC),推荐使用PASS(密码只识别前8位),主备之间必须使用相同的密码才能正常通信 auth_pass 123456 } virtual_ipaddress { # 必填,虚拟 IP,可以设置多个虚拟 IP 地址,每行一个 192.168.1.173 } }
1 2 3 4 5 sudo systemctl start keepalived sudo systemctl status keepalived
双机热备方案测试 在其他电脑上,如果可以通过虚拟 IP 192.168.1.173
正常访问 Haproxy 的 8888
与 3306
端口,则说明 PXC + Haproxy + Keepalived 的高可用集群搭建成功。 测试内容 虚拟 IP 端口 测试命令 Haproxy 的监控页面 192.168.1.173 8888 curl -basic -u admin:admin -I http://192.168.1.173:8888/dbs
Haproxy 的 MySQL 负载均衡 192.168.1.173 3306 mysql -h 192.168.1.173 -u uatOption -P 3306 -p
通过 SSH 分别登录进两台 Haproxy 服务器,检查两个 Keepalived 节点之间是否可以正常进行心跳通信。如果不能进行心跳通信,则会发生脑裂现象(即两个 Keepalived 节点会同时抢占到虚拟 IP)。 关闭 Haproxy 节点一服务器上的 Keepalived 服务,然后在其他电脑上,打开浏览器访问 http://192.168.1.173:8888/dbs
。如果可以正常访问 Haproxy 的监控页面,则说明 Haproxy + Keepalived 的双机热备方案生效了。 1 2 sudo systemctl stop keepalived
在 Haproxy 节点一服务器上的 Keepalived 服务关闭之后,通过 SSH 登录进 Haproxy 节点二服务器,使用 ip addr
命令查看 IP 地址,可以观察到 Haproxy 节点二服务器已经抢占到虚拟 IP。 双机热备方案完善 第一个问题:上述两个 Haproxy 服务器内的 Keepalived 服务,彼此仅仅是基于心跳检测来实现双机热备(故障切换)。如果第一个 Haproxy 服务器内的 Keepalived 服务(Master)正常运行,而 Haproxy 自身运行异常,那么将会出现 Haproxy 负载均衡服务失效,无法切换到备用的 Haproxy 负载均衡器上,最终导致后端的 Web 服务无法收到响应。所以,应该是要基于 Shell 脚本每隔一段时间检测 Haproxy 服务是否正常运行,而不是仅仅依靠 Keepalived 主备节点之间的心跳检测。 比如,当检测到 Haproxy 服务不是正常运行,首先尝试启动 Haproxy 服务;若 Haproxy 服务重启失败,就应该关闭掉该节点上的 Keepalived 服务,并发送报警邮件,这样才能自动切换到 Keepalived 服务的 Backup 节点上。详细的解决方案建议参考 这里 的教程。
第二个问题:Haproxy 代理 MySQL 的时候,事务持久性的问题必须解决。这个事务持久性不是 ACID 的 D(持久性,Durability),而是 Transaction Persistent,这里简单描述一下此处的事务持久性。
1 2 3 4 5 start transaction update1... update2... insert3... commit
当客户端显式开启一个事务,然后执行上述几个数据库操作,然后提交或回滚。如果使用代理软件(如 Haproxy)对 MySQL 进行代理,必须要保证这 5 个语句全都路由到同一个 MySQL 节点上,即使后端的 MySQL 采用的是多主模型(MGR、Galera 都提供多主模型),否则事务中各语句分散,轻则返回失败,重则数据不一致、提交混乱。这就是 Transaction Persistent 的概念,即让同一个事务路由到同一个后端节点。Haproxy 如何保证事务持久性呢?对于非 MySQL 协议感知的代理(LVS、Nginx、Haproxy 等),要保证事务持久性,只能通过间接的方法实现,比较通用的方法是在代理软件上监听不同的端口(实现读写分离)。具体的思路如下:
1)在 Haproxy 上监听不同端口,例如 3307
端口的请求作为写端口,3306
端口的请求作为读端口。 2)从后端 MySQL 节点中选一个节点 (只能是一个) 作为逻辑写节点,Haproxy 将 3307
端口的请求全都路由给这个节点。 3)可以在 Haproxy 上配置多个备用写节点 (Backup),但 3307
端口在某一时刻,路由到的必须只能有一个写节点。 这样能保证事务的持久性,也能解决一些乐观锁问题。但是,如果后端是多主模型的 MGR(组复制)或 Galera,这样的代理方式将强制变为单主模型,虽然是逻辑上的强制。当然,这并非什么问题,至少到目前为止的开源技术,都建议采用单主模型。Haproxy 保证事务持久性的配置示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 listen haproxy_3306_read_multi bind *:3306 mode tcp timeout client 10800s timeout server 10800s balance leastconn option httpchk option allbackups default-server port 9200 inter 2s downinter 5s rise 3 fall 2 slowstart 60s maxconn 64 maxqueue 128 weight 100 server galera1 192.168.55.111:3306 check server galera2 192.168.55.112:3306 check server galera3 192.168.55.113:3306 check listen haproxy_3307_write_single bind *:3307 mode tcp timeout client 10800s timeout server 10800s balance leastconn option httpchk option allbackups default-server port 9200 inter 2s downinter 5s rise 3 fall 2 slowstart 60s maxconn 64 maxqueue 128 weight 100 server galera1 192.168.55.111:3306 check server galera2 192.168.55.112:3306 check backup server galera3 192.168.55.113:3306 check backup
上面的配置通过 3306
端口和 3307
端口进行读写分离,并且在负责写的 3307
端口中只有一个节点可写,其余两个节点作为 Backup 节点。对于 MySQL 的负载来说,更建议采用 MySQL 协议感知的程序来实现,例如 MySQL Router、ProxySql,MaxScale、MyCat 等数据库中间件。
PXC 集群运维 查看错误日志 PXC 集群使用的是 Percona Server 数据库,它是 MySQL 的衍生版本,因此 PXC 集群节点的很多操作跟 MySQL 一样,其错误日志信息存放在每个节点的 /var/log/mysql/error.log
文件里面。
1 2 sudo vi /var/log /mysql/error.log
正确关闭集群 如果第一个节点(负责集群初始化)不是最后一个离开集群的,那么它在一般情况下就不能再以第一个节点的形式启动了。这是因为从这个节点引导集群启动可能是不安全的,即这个节点可能不包含所有更新的数据。综上所述,PXC 集群节点的正确关闭顺序,应该与它们的启动顺序相反(类似栈结构 - 先进后出),即最先启动的节点应该最后关闭。
1 2 3 4 5 6 7 8 sudo systemctl start mysql@bootstrap.service sudo systemctl start mysql sudo systemctl start mysql
1 2 3 4 5 6 7 8 sudo systemctl stop mysql sudo systemctl stop mysql sudo systemctl stop mysql@bootstrap.service
节点动态下线 PXC 集群允许动态下线节点,但需要注意的是节点的启动命令和关闭命令必须一致;比如使用 mysql@bootstrap.service
服务启动的第一个节点服务器(负责集群初始化),在它关闭时也必须使用 mysql@bootstrap.service
服务来操作(该结论有待验证 )。
1 2 3 4 5 sudo systemctl stop mysql@bootstrap.service sudo systemctl start mysql@bootstrap.service
1 2 3 4 5 sudo service stop mysql sudo service start mysql
提示
由于 PXC 集群的所有节点都是对等的,所以下线第一个节点和下线其他节点在效果上都是相同的。
单节点的操作系统重启 特别注意,当 PXC 集群中某个节点所在的 Debian 系统重启后,PXC 会自动将该节点的 MySQL 服务从集群中剔除(因为节点不可用了),以此保证高可用,但该节点的 MySQL 服务后续是不会自动启动的。 也就是说,等 Debian 系统重启完成后,必须手动启动该节点上的 MySQL 服务。此时只要确保 PXC 集群中至少有一个节点存活着,那么就不需要再重新初始化 PXC 集群(引导 PXC 集群启动),因此无论等待重启的节点是第一个节点(负责集群初始化),还是其他节点,都可以直接使用 mysql
服务进行启动。
1 2 sudo service start mysql
集群断电重启,第一个节点启动失败 PXC 集群的三台节点服务器都突然断电重启了(即所有集群节点几乎都在同一时刻意外关闭),等服务器上的 Debian 系统相继重新启动完成后,无法使用以下命令正常启动第一个节点服务器(负责集群初始化)上的 MySQL 服务 1 2 sudo systemctl start mysql@bootstrap.service
1 2 systemctl start mysql@bootstrap.service Job for mysql@bootstrap.service failed because the control process exited with error code. See "systemctl status mysql@bootstrap.service" and "journalctl -xe" for details.
首先使用以下几种方式,查看 MySQL 的日志信息 1 2 3 4 5 6 7 8 journalctl -xe sudo vi /var/log /mysql/error.log sudo systemctl status mysql@bootstrap.service
进一步在 MySQL 的错误日志文件中,发现了以下错误内容(留意 ERROR 部分) 1 2 3 4 5 6 7 8 9 2023-10-26T22:13:35.822653Z 0 [Note] [MY-000000] [Galera] ####### Assign initial position for certification: e4bb8bf0-73d5-11ee-b279-3a5ebcc11ef7:29, protocol version: -1 2023-10-26T22:13:35.822752Z 0 [Note] [MY-000000] [WSREP] Starting replication 2023-10-26T22:13:35.822790Z 0 [Note] [MY-000000] [Galera] Connecting with bootstrap option: 1 2023-10-26T22:13:35.822809Z 0 [Note] [MY-000000] [Galera] Setting GCS initial position to e4bb8bf0-73d5-11ee-b279-3a5ebcc11ef7:29 2023-10-26T22:13:35.822823Z 0 [ERROR] [MY-000000] [Galera] It may not be safe to bootstrap the cluster from this node. It was not the last one to leave the cluster and may not contain all the updates. To force cluster bootstrap with this node, edit the grastate.dat file manually and set safe_to_bootstrap to 1 . 2023-10-26T22:13:35.822844Z 0 [ERROR] [MY-000000] [WSREP] Provider/Node (gcomm://192.168.1.188,192.168.1.193,192.168.1.223) failed to establish connection with cluster (reason: 7) 2023-10-26T22:13:35.822855Z 0 [ERROR] [MY-010119] [Server] Aborting 2023-10-26T22:13:35.823148Z 0 [System] [MY-010910] [Server] /usr/sbin/mysqld: Shutdown complete (mysqld 8.0.33-25.1) Percona XtraDB Cluster (GPL), Release rel25, Revision 0c56202, WSREP version 26.1.4.3. 2023-10-26T22:13:35.824298Z 0 [ERROR] [MY-010065] [Server] Failed to shutdown components infrastructure.
意思是从这个节点引导集群启动可能是不安全。由于该节点不是最后一个离开集群的节点(最后停掉的节点),可能不包含所有更新的数据。要强制使用该节点进行集群引导,请手动编辑该节点的 grastate.dat
文件,并将 safe_to_bootstrap
参数设置为 1
。当然了,一般情况下不需要强制从该节点启动,可以逐一排查每个节点下的 grastate.dat
文件,找到 safe_to_bootstrap=1
的节点,然后在该节点上引导 PXC 集群启动即可。如果所有节点的 safe_to_bootstrap
都为 0
,那么只能任意选择一个节点,更改该节点下的 grastate.dat
文件,将 safe_to_bootstrap
设置为 1
,然后在该节点上引导 PXC 集群启动。特别注意,引导 PXC 集群启动(第一个节点)使用的是 sudo systemctl start mysql@bootstrap.service
命令,而启动其他节点使用的则是 sudo systemctl start mysql
命令。必须等待第一个节点启动成功,也就是 PXC 集群初始化完成之后,才能接着启动其他节点,最后再检查集群的数据是否可以正常同步。
提示
grastate.dat
文件的完整路径是 /var/lib/mysql/grastate.dat
。在第一个节点服务器上引导 PXC 集群启动(即集群初始化)的命令是 sudo systemctl start mysql@bootstrap.service
,其他节点的 MySQL 服务启动命令则是 sudo systemctl start mysql
。 参考资料