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,
再安装主节点启动