MySQL分布式高可用-PXC
PXC是percona公司的Percona XtraDB Cluster,简称PXC。它是基于GaLera协议的高可用集群方案。可以实现多个节点间的数据同步复制以及读写,并且可保障数据库的服务高可用及数据强一致性。
PXC属于一套近乎完美的mysql高可用集群解决方案,相比那些比较传统的基于主从复制模式的集群架构MHA和MM+keepalived,galera cluster最突出特点就是解决了诟病已久的数据复制延迟问题,基本上可以达到实时同步。而且节点与节点之间,他们相互的关系是对等的。本身galera cluster也是一种多主架构。galera cluster最关注的是数据的一致性,对待事物的行为时,要么在所有节点上执行,要么都不执行,它的实现机制决定了它对待一致性的行为非常严格,这也能非常完美的保证MySQL集群的数据一致性;
对galera cluster的封装有两个,虽然名称不同,但实质都是一样的,使用的都是galera cluster。一个MySQL的创始人在自己全新的MariaDB上实现的MariaDB cluster;一个是著名的MySQL服务和工具提供商percona实现的percona xtradb cluster,简称PXC。
一、基础原理
Galera是Codership提供的多主数据同步复制机制,可以实现多个节点间的数据同步复制以及读写,并且可保障数据库的服务高可用及数据一致。
PXC架构示意图:
当client端执行dml操作时,将操作发给server,server的native进程处理请求,client端执行commit,server将复制写数据集发给group(cluster),cluster中每个动作对应一个GTID,其它server接收到并通过验证(合并数据)后,执行appyl_cb动作和commit_cb动作;若验证没通过,则会退出处理;
当前server节点验证通过后,执行commit_cb,并返回,若没通过,执行rollback_cb。只要当前节点执行了commit_cb和其它节点验证通过后就可返回。
用户发起Commit,在收到Ok之前,集群每次发起一个动作,都会有一个唯一的编号 ,也就是PXC独有的Global Trx Id。动作发起者是commit_cb,其它节点多了一个动作: apply_cb
任意节点收到sql请求,对于DML更新操作事务,在commit之前,由wsrep API调用galera库进行集群内部广播,验证当前事务是否能在所有节点中执行,验证通过后该事务真正提交到集群所有节点执行,反之rollback回滚。
优点:
- 服务高可用,多个节点的数据是相同的,只要最后一个节点可用,就还能运行。无需集中管理。可以在任何时间点失去任何节点,但是集群将照常工作,不受影响。
- 同步复制,事务要么在所有节点提交或不提交。各节点间无延迟且节点宕机不会导致数据丢失。
- 多主复制,可以在任意节点进行写操作。
- 在从服务器上并行应用事件,真正意义上的并行复制。
- 节点自动配置。无需手工备份当前数据库并拷贝至新节点。
- 数据一致性,不再是异步复制。
- PXC最大的优势:强一致性、无同步延迟
- 应用程序的兼容性:无需更改应用程序
缺点:
- 对InnoDB的事物控制是支持的,其他的不支持;
- 加入新节点,开销大。需要复制完整的数据。新加入节点采用SST时代价高;
- 不能有效的解决写缩放问题,所有的写操作都将发生在所有节点上。
- 有多少个节点就有多少重复的数据。
- 写入效率取决于节点中最弱的一台,因为PXC集群采用的是强一致性原则,一个更改操作在所有节点都成功才算执行成功。集群吞吐量/性能取决于短板;
- 所有表都要有主键;
- 锁冲突、死锁问题相对更多;
- 不支持LOCK TABLE等显式锁操作;
- 不支持XA;
PXC会使用4个端口号
- 3306:数据库对外服务的端口号;
- 4444:请求SST SST: 指数据一个镜象传输 xtrabackup , rsync ,mysqldump 在新节点加入时起作用;
- 4567: 组成员之间进行沟通的一个端口号;
- 4568: 传输IST用的。相对于SST来说的一个增量,节点下线,重启加入时起作用
#名词解释
WS:write set 写数据集
IST: Incremental State Transfer 增量同步
SST:State Snapshot Transfer 全量同步
二、配置PXC集群
实验环境:
主机名 | IP | 系统版本 | 软件版本 |
---|---|---|---|
mysql01 | 192.168.154.101 | Centos 7.7 | |
mysql02 | 192.168.154.102 | Centos 7.7 | |
mysql03 | 192.168.154.103 | Centos 7.7 |
2.1 基础环境
关闭Selinux
[root@mysql02 ~]# setenforce 0
防火墙放行以下端口:
[root@mysql02 ~]# firewall-cmd --add-port=3306/tcp --permanent
success
[root@mysql02 ~]# firewall-cmd --add-port=4444/tcp --permanent
success
[root@mysql02 ~]# firewall-cmd --add-port=4567/tcp --permanent
success
[root@mysql02 ~]# firewall-cmd --add-port=4568/tcp --permanent
success
[root@mysql02 ~]# firewall-cmd --reload
success
卸载主机上的MySQL或者mariadb
2.2 安装
先安装官方YUM源
yum install https://repo.percona.com/yum/percona-release-latest.noarch.rpm
安装程序
[root@mysql01 ~]# yum install Percona-XtraDB-Cluster-57 -y
如果在线安装比较慢,可以下载离线包
下载地址:https://www.percona.com/downloads/Percona-XtraDB-Cluster-LATEST/
启动Percona XtraDB Cluster server:
[root@mysql01 percona]# service mysql start
修改root密码
[root@mysql01 ~]# grep 'temporary password' /var/log/mysqld.log
2020-01-09T09:28:03.748525Z 1 [Note] A temporary password is generated for root@localhost: vr?OHmjy2kel
[root@mysql01 ~]# mysql -uroot -p
Enter password:
mysql> alter user 'root'@'localhost' identified by 'Com.123456';
Query OK, 0 rows affected (0.00 sec)
mysql> exit
停止服务
[root@mysql01 ~]# systemctl stop mysql.service
2.3 为写集复制配置相关请求
设置配置文件如下:
[root@mysql01 ~]# grep -v '^#' /etc/percona-xtradb-cluster.conf.d/wsrep.cnf
[mysqld]
#指定galera库路径
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
#指定群集中节点的IP地址。节点加入群集至少需要一个,但建议列出所有节点的地址。
wsrep_cluster_address=gcomm://192.168.154.101,192.168.154.102,192.168.154.103
#Galera只支持行级复制
binlog_format=ROW
#Galera完全支持InnoDB存储引擎。它与MyISAM或任何其他非事务性存储引擎一起时将不工作
default_storage_engine=InnoDB
wsrep_slave_threads= 8
wsrep_log_conflicts
##主键自增长不锁表 只能设置为2,设置为0或1时会无法正确处理死锁问题
innodb_autoinc_lock_mode=2
#指定群集的逻辑名称。集群中的所有节点都必须相同。
wsrep_cluster_name=pxc-cluster
#为每个节点指定逻辑名称。如果未指定此变量,则主机名将会被使用。
wsrep_node_name=mysql01
#节点IP地址
wsrep_node_address=192.168.154.101
#同步严厉模式 限制PXC启用正在试用阶段的功能,ENFORCING是默认值。
pxc_strict_mode=ENFORCING
#设置SST同步方法,默认情况下,Percona XtraDB集群使用Percona XtraBackup进行状态快照传输(SST)
wsrep_sst_method=xtrabackup-v2
#设置SST同步用户和密码,此用户必须要创建
wsrep_sst_auth="sstuser:s3cretPass"
创建SST同步账号并授权:
mysql> create user 'sstuser'@'localhost' identified by 's3cretPass';
Query OK, 0 rows affected (0.01 sec)
mysql> grant reload,lock tables, process, replication client on *.* to 'sstuser'@'localhost';
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.05 sec)
另外两个节点做相同的配置,不同之处就是名称和IP地址,还有数据库的server_id也不能相同
#第二个节点
server-id=2
wsrep_node_name=mysql02
wsrep_node_address=192.168.154.102
#第三个节点
server-id=3
wsrep_node_name=mysql03
wsrep_node_address=192.168.154.103
2.4 第一个节点自举启动,初始化集群
配置完所有PXC节点后,通过引导第一个节点来初始化集群。
[root@mysql01 ~]# systemctl start mysql@bootstrap.service
要确保群集已初始化,请运行以下命令:
mysql> show status like 'wsrep%';
+----------------------------------+---------------------------------------------+
| Variable_name | Value |
+----------------------------------+---------------------------------------------+
| wsrep_local_state_uuid | 5ab0ecc8-32c2-11ea-bb6e-f3af6a7fda56 |
| wsrep_protocol_version | 9 |
| wsrep_last_applied | 1 |
| wsrep_last_committed | 1 |
| wsrep_replicated | 0 |
| wsrep_replicated_bytes | 0 |
| wsrep_repl_keys | 0 |
| wsrep_repl_keys_bytes | 0 |
| wsrep_repl_data_bytes | 0 |
| wsrep_repl_other_bytes | 0 |
| wsrep_received | 2 |
| wsrep_received_bytes | 146 |
| wsrep_local_commits | 0 |
| wsrep_local_cert_failures | 0 |
| wsrep_local_replays | 0 |
| wsrep_local_send_queue | 0 |
| wsrep_local_send_queue_max | 1 |
| wsrep_local_send_queue_min | 0 |
| wsrep_local_send_queue_avg | 0.000000 |
| wsrep_local_recv_queue | 0 |
| wsrep_local_recv_queue_max | 1 |
| wsrep_local_recv_queue_min | 0 |
| wsrep_local_recv_queue_avg | 0.000000 |
| wsrep_local_cached_downto | 0 |
| wsrep_flow_control_paused_ns | 0 |
| wsrep_flow_control_paused | 0.000000 |
| wsrep_flow_control_sent | 0 |
| wsrep_flow_control_recv | 0 |
| wsrep_flow_control_interval | [ 100, 100 ] |
| wsrep_flow_control_interval_low | 100 |
| wsrep_flow_control_interval_high | 100 |
| wsrep_flow_control_status | OFF |
| wsrep_cert_deps_distance | 0.000000 |
| wsrep_apply_oooe | 0.000000 |
| wsrep_apply_oool | 0.000000 |
| wsrep_apply_window | 0.000000 |
| wsrep_commit_oooe | 0.000000 |
| wsrep_commit_oool | 0.000000 |
| wsrep_commit_window | 0.000000 |
| wsrep_local_state | 4 |
| wsrep_local_state_comment | Synced |
| wsrep_cert_index_size | 0 |
| wsrep_cert_bucket_count | 22 |
| wsrep_gcache_pool_size | 1320 |
| wsrep_causal_reads | 0 |
| wsrep_cert_interval | 0.000000 |
| wsrep_open_transactions | 0 |
| wsrep_open_connections | 0 |
| wsrep_ist_receive_status | |
| wsrep_ist_receive_seqno_start | 0 |
| wsrep_ist_receive_seqno_current | 0 |
| wsrep_ist_receive_seqno_end | 0 |
| wsrep_incoming_addresses | 192.168.154.101:3306 |
| wsrep_cluster_weight | 1 |
| wsrep_desync_count | 0 |
| wsrep_evs_delayed | |
| wsrep_evs_evict_list | |
| wsrep_evs_repl_latency | 1.572e-06/3.154e-06/5.712e-06/1.51575e-06/5 |
| wsrep_evs_state | OPERATIONAL |
| wsrep_gcomm_uuid | ddc20147-32c6-11ea-aa40-ebb3a466f894 |
| wsrep_cluster_conf_id | 1 |
| wsrep_cluster_size | 1 |
| wsrep_cluster_state_uuid | 5ab0ecc8-32c2-11ea-bb6e-f3af6a7fda56 |
| wsrep_cluster_status | Primary |
| wsrep_connected | ON |
| wsrep_local_bf_aborts | 0 |
| wsrep_local_index | 0 |
| wsrep_provider_name | Galera |
| wsrep_provider_vendor | Codership Oy <info@codership.com> |
| wsrep_provider_version | 3.41(rb3295e6) |
| wsrep_ready | ON |
+----------------------------------+---------------------------------------------+
71 rows in set (0.00 sec)
重点关注:
# wsrep_local_state_uuid
# wsrep_local_state
# wsrep_local_state_comment
# wsrep_cluster_size
# wsrep_cluster_status
# wsrep_connected
# wsrep_ready
2.5 添加其它节点到集群中
集群中的节点会自动加入并同步
[root@mysql02 ~]# systemctl start mysql
[root@mysql02 ~]# systemctl status mysql
● mysql.service - Percona XtraDB Cluster
Loaded: loaded (/usr/lib/systemd/system/mysql.service; enabled; vendor preset: disabled)
Active: active (running) since 四 2020-01-09 19:23:49 CST; 10s ago
Process: 2498 ExecStopPost=/usr/bin/mysql-systemd stop-post (code=exited, status=0/SUCCESS)
Process: 2463 ExecStop=/usr/bin/mysql-systemd stop (code=exited, status=0/SUCCESS)
Process: 6005 ExecStartPost=/usr/bin/mysql-systemd start-post $MAINPID (code=exited, status=0/SUCCESS)
Process: 5963 ExecStartPre=/usr/bin/mysql-systemd start-pre (code=exited, status=0/SUCCESS)
Main PID: 6004 (mysqld_safe)
CGroup: /system.slice/mysql.service
├─6004 /bin/sh /usr/bin/mysqld_safe --basedir=/usr
└─6440 /usr/sbin/mysqld --basedir=/usr --datadir=/var/lib/mysql --plugin-dir=/usr/lib64/mysql/plugin --user=mysql ...
1月 09 19:23:27 mysql02 systemd[1]: Starting Percona XtraDB Cluster...
1月 09 19:23:27 mysql02 mysqld_safe[6004]: 2020-01-09T11:23:27.606599Z mysqld_safe Logging to '/var/log/mysqld.log'.
1月 09 19:23:27 mysql02 mysqld_safe[6004]: 2020-01-09T11:23:27.610926Z mysqld_safe Logging to '/var/log/mysqld.log'.
1月 09 19:23:27 mysql02 mysqld_safe[6004]: 2020-01-09T11:23:27.633970Z mysqld_safe Starting mysqld daemon with databas.../mysql
1月 09 19:23:27 mysql02 mysqld_safe[6004]: 2020-01-09T11:23:27.644277Z mysqld_safe Skipping wsrep-recover for 84b9100f...1 pair
1月 09 19:23:27 mysql02 mysqld_safe[6004]: 2020-01-09T11:23:27.645386Z mysqld_safe Assigning 84b9100f-32c4-11ea-8bbb-a...sition
1月 09 19:23:29 mysql02 mysql-systemd[6005]: State transfer in progress, setting sleep higher
1月 09 19:23:49 mysql02 mysql-systemd[6005]: SUCCESS!
1月 09 19:23:49 mysql02 systemd[1]: Started Percona XtraDB Cluster.
Hint: Some lines were ellipsized, use -l to show in full.
服务器启动后,应自动接收SST。
要检查第二个节点的状态,请运行以下命令:
mysql> show status like 'wsrep%';
+----------------------------------+-------------------------------------------+
| Variable_name | Value |
+----------------------------------+-------------------------------------------+
| wsrep_local_state_uuid | 5ab0ecc8-32c2-11ea-bb6e-f3af6a7fda56 |
| wsrep_protocol_version | 9 |
| wsrep_last_applied | 4 |
| wsrep_last_committed | 4 |
| wsrep_replicated | 0 |
| wsrep_replicated_bytes | 0 |
| wsrep_repl_keys | 0 |
| wsrep_repl_keys_bytes | 0 |
| wsrep_repl_data_bytes | 0 |
| wsrep_repl_other_bytes | 0 |
| wsrep_received | 3 |
| wsrep_received_bytes | 228 |
| wsrep_local_commits | 0 |
| wsrep_local_cert_failures | 0 |
| wsrep_local_replays | 0 |
| wsrep_local_send_queue | 0 |
| wsrep_local_send_queue_max | 1 |
| wsrep_local_send_queue_min | 0 |
| wsrep_local_send_queue_avg | 0.000000 |
| wsrep_local_recv_queue | 0 |
| wsrep_local_recv_queue_max | 1 |
| wsrep_local_recv_queue_min | 0 |
| wsrep_local_recv_queue_avg | 0.000000 |
| wsrep_local_cached_downto | 0 |
| wsrep_flow_control_paused_ns | 0 |
| wsrep_flow_control_paused | 0.000000 |
| wsrep_flow_control_sent | 0 |
| wsrep_flow_control_recv | 0 |
| wsrep_flow_control_interval | [ 141, 141 ] |
| wsrep_flow_control_interval_low | 141 |
| wsrep_flow_control_interval_high | 141 |
| wsrep_flow_control_status | OFF |
| wsrep_cert_deps_distance | 0.000000 |
| wsrep_apply_oooe | 0.000000 |
| wsrep_apply_oool | 0.000000 |
| wsrep_apply_window | 0.000000 |
| wsrep_commit_oooe | 0.000000 |
| wsrep_commit_oool | 0.000000 |
| wsrep_commit_window | 0.000000 |
| wsrep_local_state | 4 |
| wsrep_local_state_comment | Synced |
| wsrep_cert_index_size | 0 |
| wsrep_cert_bucket_count | 22 |
| wsrep_gcache_pool_size | 1456 |
| wsrep_causal_reads | 0 |
| wsrep_cert_interval | 0.000000 |
| wsrep_open_transactions | 0 |
| wsrep_open_connections | 0 |
| wsrep_ist_receive_status | |
| wsrep_ist_receive_seqno_start | 0 |
| wsrep_ist_receive_seqno_current | 0 |
| wsrep_ist_receive_seqno_end | 0 |
| wsrep_incoming_addresses | 192.168.154.102:3306,192.168.154.101:3306 |
| wsrep_cluster_weight | 2 |
| wsrep_desync_count | 0 |
| wsrep_evs_delayed | |
| wsrep_evs_evict_list | |
| wsrep_evs_repl_latency | 0/0/0/0/0 |
| wsrep_evs_state | OPERATIONAL |
| wsrep_gcomm_uuid | 753e078e-32d2-11ea-85b2-7669b28d076a |
| wsrep_cluster_conf_id | 2 |
| wsrep_cluster_size | 2 |
| wsrep_cluster_state_uuid | 5ab0ecc8-32c2-11ea-bb6e-f3af6a7fda56 |
| wsrep_cluster_status | Primary |
| wsrep_connected | ON |
| wsrep_local_bf_aborts | 0 |
| wsrep_local_index | 0 |
| wsrep_provider_name | Galera |
| wsrep_provider_vendor | Codership Oy <info@codership.com> |
| wsrep_provider_version | 3.41(rb3295e6) |
| wsrep_ready | ON |
+----------------------------------+-------------------------------------------+
71 rows in set (0.00 sec)
使用同样的方法加入第三个节点即可
2.6 检查复制
将所有节点添加到群集时,可以通过运行查询和操作节点上的数据来验证复制,以查看这些更改是否在集群中同步。
在第二个节点上创建一个数据库
mysql> CREATE DATABASE percona;
Query OK, 1 row affected (0.01 sec)
在第三个节点上创建一张表
mysql> use percona;
Database changed
mysql> CREATE TABLE example (node_id INT PRIMARY KEY, node_name VARCHAR(30));
Query OK, 0 rows affected (0.02 sec)
在第一个节点上插入数据
mysql> INSERT INTO percona.example VALUES (1, 'percona1');
Query OK, 1 row affected (0.01 sec)
从第二个节点上的表中检索行
mysql> SELECT * FROM percona.example;
+---------+-----------+
| node_id | node_name |
+---------+-----------+
| 1 | percona1 |
+---------+-----------+
1 row in set (0.00 sec)
2.7 安装ProxySQL
节点加入问题
旧节点加入到集群中:
如果旧节点加入Galera集群,说明这个节点在之前已经在Galera集群中呆过,有一部分数据基础,缺少的只是它离开集群时的数据。这时加入集群时,会采用IST(incremental snapshot transfer)传输机制,即使用增量传输。但注意,这部分增量传输的数据源是Donor上缓存在GCache文件中的,这个文件有大小限制,如果缺失的数据范围超过已缓存的内容,则自动转为SST传输。如果旧节点上的数据和Donor
上的数据不匹配(例如这个节点离组后人为修改了一点数据),则自动转为SST传输。
节点在退出集群后,从新加入的时候,如果这个故障节点的ip在自己的配置文件 wsrep_cluster_address的选项中的第一个ip,那么这个节点是永远都无法再加入这个集群了。参考:http://blog.itpub.net/133735/viewspace-2140548/
若是主节点,IP不能放在第一个,不能再用开始启动主节点的方式来进行启动 。之前的从节点重启后就加入到集群中去了,没啥特殊。
新节点加入到集群中:
新节点加入集群时,需要从当前集群中选择一个Donor节点来同步数据,也就是所谓的state_snapshot_tranfer(SST)过程。SST同步数据的方式由选项wsrep_sst_method决定,一般选择的是xtrabackup。
wsrep_sst_method:state_snapshot_transfer(SST)使用的传输方法,可用方法有mysqldump、rsync和xtrabackup,前两者在传输时都需要对Donor加全局只读锁(FLUSH TABLES WITH READ LOCK),xtrabackup则不需要(它使用percona自己提供的backup lock)。强烈建议采用xtrabackup。
必须注意,新节点加入Galera时,会删除新节点上所有已有数据,再通过xtrabackup(假设使用的是该方式)从Donor处完整备份所有数据进行恢复。所以,如果数据量很大,新节点加入过程会很慢。而且,在一个新节点成为Synced状态之前,不要同时加入其它新节点,否则很容易将集群压垮。如果是这种情况,可以考虑使用wsrep_sst_method=rsync来做增量同步,既然是增量同步,最好保证新节点上已经有一部分数据基础,否则和全量同步没什么区别,且这样会对Donor节点加上全局read only锁。
- OPEN:节点启动成功,尝试连接到集群,如果失败则根据配置退出或创建新的集群
- PRIMARY:节点已处于集群中,在新节点加入时,选取donor进行数据同步时会产生的状态
- JOINER:节点处于等待接收/接收同步文件时的状态
- JOINED:节点完成数据同步,但有部分数据没跟上,在尝试保持和集群进度一致的过程状态,例如某个节点故障后,重新加入集群,在追赶集群进度时的状态
- SYNCED:节点正常提供服务的状态,表示已经同步完成并和集群进度保持一致。
- DONOR:节点处于为新节点提供全量数据数据同步时的状态。此时该节点对客户端不提供服务。
PXC节点启动与关闭
如果最后关闭的PXC节点是安全退出的,那么下次启动要最先启动这个节点,而且要以主节点启动
如果最后关闭的PXC节点不是安全退出的,
那么要先修改/var/lib/mysql/grastate.dat 文件,
把其中的safe_to_bootstrap属性值设置为1,
再安装主节点启动