本博客讨论PXC集群架构。
1.架构说明
PXC是Percona Xtradb Cluster的缩写,是由业界著名的Percona公司提供的一种MySQL高可用集群架构,PXC架构的最大的特点在于,每个MySQL节点都有一份完整数据的拷贝,每个MySQL节点都可以对外提供读写服务,相当于每个MySQL节点都是主节点。正是由于PXC节点的这个特点,在某个节点出现故障时,直接将该节点下线并踢出集群即可,不影响其它节点的正常服务。
2.核心原理
PXC集群中第一个节点作为启动节点,建立群组,其它节点加入到该群组中。
每个节点维护一份最近操作的写数据集,主要是内存中,还可以存在于文件当中。当客户端通过某个节点写入数据时,先执行本地事物,再将该写数据集发送到其它节点,其它节点经过验证后可以提交,则返回一个状态,本地真正提交事务,并返回执行结果给客户端。PXC集群通过这种方式保证了集群中各个节点的数据的一致性。
当新节点和现节点之间的差异较大时,新节点可进行SST复制(State snapped transfer,快照状态转换),当差异较小时,可使用IST复制(Increasement state transfer,增量状态转换),二者各自使用独立的通信端口。使用percona xtrabackup工具进行数据的拷贝工作,实现新节点的快速加入集群。PXC集群的MySQL节点之间的数据传递机制,并不是传统的MySQL复制技术,而是基于gcache技术。
每个节点可能的状态有以下几个:
synced:已同步状态。相当于MySQL NDB Cluster中的Online状态,即服务处于在线状态。
donored:供体状态。该节点正在对其它某个新节点提供数据传输服务(SST/IST)。
desynced:未同步状态。相当于MySQL NDB Cluster中的Offline状态,即服务离线状态。
3.架构缺点
PXC集群中每个事务写入的数据,都需要通过网络传输到其它每个节点并进行存储,使得集群中每个主机的写事务的效率有所降低。
PXC集群中有流量控制机制,对单个事务的数据量有所限制,当触发流量控制机制时会进行一段时间的限流操作,这也会影响集群的事务的执行效率。
PXC集群中每个节点拥有完整数据,使得这种集群的数据安全有充分的保障,同时也带来消耗存储空间较大的问题。
PXC集群要求每个表必须有一个主键,否则可能造成集群故障。
PXC集群只能使用Innodb存储引擎,不支持MyIASM等不支持事务的存储引擎。
单纯的PXC集群无法对外提供负载均衡功能。
4.适用场景
PXC集群适用于对事务一致性要求较高但是事务数据量不大的应用场合。
PXC集群不适合高并发的大量写事务的同时对写事务效率有较高要求的应用场合。
PXC集群不适合数据量特别巨大的应用场合。
5.搭建环境
搭建PXC集群环境的主要步骤如下:
准备安装包和依赖包,并进行安装。
确定集群的节点并修改PXC的配置文件。
初始化MySQL数据库。
启动PXC集群。
下面开始具体介绍PXC集群的主要步骤。
(1)准备安装包和依赖包,并进行安装。
从Percona官网下载Percona-XtraDB-Cluster-5.7.22-29.26-r422-el7-x86_64-bundle.tar文件并解压缩。
[root@201 pxc]# ls rpm
Percona-XtraDB-Cluster-57-5.7.22-29.26.1.el7.x86_64.rpm Percona-XtraDB-Cluster-garbd-57-5.7.22-29.26.1.el7.x86_64.rpm
Percona-XtraDB-Cluster-57-debuginfo-5.7.22-29.26.1.el7.x86_64.rpm Percona-XtraDB-Cluster-server-57-5.7.22-29.26.1.el7.x86_64.rpm
Percona-XtraDB-Cluster-client-57-5.7.22-29.26.1.el7.x86_64.rpm Percona-XtraDB-Cluster-shared-57-5.7.22-29.26.1.el7.x86_64.rpm
Percona-XtraDB-Cluster-devel-57-5.7.22-29.26.1.el7.x86_64.rpm Percona-XtraDB-Cluster-shared-compat-57-5.7.22-29.26.1.el7.x86_64.rpm
Percona-XtraDB-Cluster-full-57-5.7.22-29.26.1.el7.x86_64.rpm Percona-XtraDB-Cluster-test-57-5.7.22-29.26.1.el7.x86_64.rpm(这个包可以不安装)。
需要按照一定的次序安装这些rpm包。安装过程中会提示缺少依赖包。
其它需要安装的包如下:
libev-4.24.tar.gz
qpress-1.1-8.1.x86_64.rpm
percona-toolkit-3.0.11-1.el7.x86_64.rpm
percona-xtrabackup-24-2.4.12-1.el7.x86_64.rpm
这里由于测试环境不能连接互联网,所以相对比较繁琐。
(2).确定集群的节点并修改PXC的配置文件。
软件包安装成功之后,在/etc/目录下自动产生如下的配置文件和目录:
root@201 depends]# ls -l /etc/percona-xtradb-cluster.c
percona-xtradb-cluster.cnf percona-xtradb-cluster.conf.d/
[root@201 depends]# ls -l /etc/percona-xtradb-cluster.conf.d
total 12
-rw-r--r--. 1 root root 400 Sep 17 05:14 mysqld.cnf
-rw-r--r--. 1 root root 440 Jun 29 12:01 mysqld_safe.cnf
-rw-r--r--. 1 root root 1086 Sep 17 05:03 wsrep.cnf
大部分参数可以在各个节点中共享,需要独立修改的参数主要是:
本节点的server-id(mysqld.cnf)
本节点的IP地址和名称(wsrep.cnf)
主要需要修改一下mysqld.cnf文件中的server-id,并增加一些必备的参数,比如
[mysqld]
server-id=201
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
log-error=/var/log/mysqld.log
pid-file=/var/run/mysqld/mysqld.pid
log-bin=mysql-binlog
log_slave_updates=1
expire_logs_days=100
修改wsrep.cnf文件,主要参数如下:
配置3个节点,各个节点的IP地址和端口号(这里默认为3306)。
[root@201 depends]# cat /etc/percona-xtradb-cluster.conf.d/wsrep.cnf
[mysqld]
# Path to Galera library
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
# Cluster connection URL contains IPs of nodes
#If no IP is found, this implies that a new cluster needs to be created,
#in order to do that you need to bootstrap this node
wsrep_cluster_address=gcomm://11.1.1.201,11.1.1.202,11.1.1.203
# In order for Galera to work correctly binlog format should be ROW
binlog_format=ROW
# MyISAM storage engine has only experimental support
default_storage_engine=InnoDB
# Slave thread to use
wsrep_slave_threads= 8
wsrep_log_conflicts
# This changes how InnoDB autoincrement locks are managed and is a requirement for Galera
innodb_autoinc_lock_mode=2
# Node IP address
wsrep_node_address=11.1.1.201
# Cluster name
wsrep_cluster_name=pxc-cluster
#If wsrep_node_name 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
# SST method
wsrep_sst_method=xtrabackup-v2
#Authentication for SST method
wsrep_sst_auth="pxc:123456"
(3).初始化MySQL数据库。
初始化数据库的办法和初始化普通的mysql数据库相同。
在三个节点上都执行如下操作:
mysqld_safe --user=mysql --initialize
然后修改auto.cnf文件的server-uuid的值为一个human-readable的值,比如将最后三位修改为IP地址的最后那个数字,最好同时也是server-id的值。
(4)启动集群。
(i)先启动第1个节点11.1.1.201。
mysqld_safe --user=mysql --wsrep-new-cluster &
启动失败有可能是配置文件的参数有拼写错误或者权限不对。
启动成功后,显示:
[root@201 depends]# mysqld_safe --user=mysql --wsrep-new-cluster&
[1] 1814
[root@201 depends]# 2018-09-17T20:39:09.653194Z mysqld_safe Logging to '/var/log/mysqld.log'.
2018-09-17T20:39:09.658221Z mysqld_safe Logging to '/var/log/mysqld.log'.
2018-09-17T20:39:09.694292Z mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
2018-09-17T20:39:09.711656Z mysqld_safe Skipping wsrep-recover for 60bd9144-b9fb-11e8-b712-67f101a461c4:15 pair
2018-09-17T20:39:09.713929Z mysqld_safe Assigning 60bd9144-b9fb-11e8-b712-67f101a461c4:15 to wsrep_start_position
使用mysql客户端程序登录到这个节点中,修改root的密码,创建SST复制时的pxc用户。
查看第1个节点的状态,显示为synced(已同步)。
mysql> show global status like 'wsrep_local%';
+----------------------------+--------------------------------------+
| Variable_name | Value |
+----------------------------+--------------------------------------+
| wsrep_local_state_uuid | 60bd9144-b9fb-11e8-b712-67f101a461c4 |
| 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 | 2 |
| wsrep_local_recv_queue_min | 0 |
| wsrep_local_recv_queue_avg | 0.500000 |
| wsrep_local_cached_downto | 0 |
| wsrep_local_state | 4 |
| wsrep_local_state_comment | Synced |
| wsrep_local_bf_aborts | 0 |
| wsrep_local_index | 0 |
+----------------------------+--------------------------------------+
17 rows in set (0.02 sec)
查看集群中节点的情况,目前只有这个节点自身(11.1.1.201:3306)。
mysql> show status like 'wsrep_incoming%';
+--------------------------+-----------------+
| Variable_name | Value |
+--------------------------+-----------------+
| wsrep_incoming_addresses | 11.1.1.201:3306 |
+--------------------------+-----------------+
1 row in set (0.01 sec)
查看集群状态,目前集群处于ON状态,即正常服务状态。
mysql> show status like 'wsrep_connected';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| wsrep_connected | ON |
+-----------------+-------+
1 row in set (0.01 sec)
查看集群中节点数量,目前一个节点( wsrep_cluster_size | 1 )。
mysql> show status like 'wsrep_cluster%';
+--------------------------+--------------------------------------+
| Variable_name | Value |
+--------------------------+--------------------------------------+
| wsrep_cluster_conf_id | 1 |
| wsrep_cluster_size | 1 |
| wsrep_cluster_state_uuid | 60bd9144-b9fb-11e8-b712-67f101a461c4 |
| wsrep_cluster_status | Primary |
+--------------------------+--------------------------------------+
4 rows in set (0.01 sec)
启动其它两个节点。
mysqld_safe --user=mysql
正常启动即可。
启动成功之后,可以发现在201节点上创建的pxc用户已经自动传送到其它两个节点上了。
mysql> show status like 'wsrep_cluster%';
+--------------------------+--------------------------------------+
| Variable_name | Value |
+--------------------------+--------------------------------------+
| wsrep_cluster_conf_id | 3 |
| wsrep_cluster_size | 3 |
| wsrep_cluster_state_uuid | 60bd9144-b9fb-11e8-b712-67f101a461c4 |
| wsrep_cluster_status | Primary |
+--------------------------+--------------------------------------+
4 rows in set (0.01 sec)
mysql> show status like 'wsrep_incoming%';
+--------------------------+-------------------------------------------------+
| Variable_name | Value |
+--------------------------+-------------------------------------------------+
| wsrep_incoming_addresses | 11.1.1.202:3306,11.1.1.203:3306,11.1.1.201:3306 |
+--------------------------+-------------------------------------------------+
1 row in set (0.00 sec)
mysql> show status like 'wsrep_connected';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| wsrep_connected | ON |
+-----------------+-------+
1 row in set (0.01 sec)
可以发现现在集群上有3个节点。
可以在任意一个节点中执行一个SQL操作,然后在其他节点上验证操作的结果是否已经在该节点上生效,正常情况下每个节点上均会有一致的效果。
至此,整个集群建立并启动完毕。
(2)增加节点
增加一个新节点时,同样需要先安装PXC相关软件包。此时可以提前使用percona-xtrabackup软件从PXC集群的某个节点制作一个全备份,在新节点上进行数据恢复。之后再让这个新节点加入到集群中。
加入到集群的办法比较简单,就是将PXC相关的配置文件修改为跟其它节点类似的配置文件即可,修改server-id、server-uuid、节点IP、节点名字。
将新节点的名字加入到wsrep_cluster_address参数中。
wsrep_cluster_address=gcomm://11.1.1.201,11.1.1.202,11.1.1.203,11.1.1.204
已经启动的现有节点最好也修改一下相应的配置文件的参数,但是不需要在线修改内存中的参数,新节点启动后其它节点自动识别出新节点并修改各自的状态。
在11.1.1.204节点上启动mysql服务。
mysqld-safe --user=mysql
此时可以在每个节点上验证当前集群中的节点列表以及各个节点的状态。
6.故障转移
在已经有3个节点的情况下,删除节点11.1.1.202,即停止该节点上的mysqld服务。
此时再在其它节点上查看集群状态,可以发现11.1.1.202已经不存在于集群中了,集群当前的节点数量为2。此时不影响集群的正常服务,各种SQL语句依然可以正常执行。
mysql> show status like 'wsrep_incoming%';
+--------------------------+---------------------------------+
| Variable_name | Value |
+--------------------------+---------------------------------+
| wsrep_incoming_addresses | 11.1.1.203:3306,11.1.1.201:3306 |
+--------------------------+---------------------------------+
1 row in set (0.00 sec)
mysql> show status like 'wsrep_connected';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| wsrep_connected | ON |
+-----------------+-------+
1 row in set (0.00 sec)
mysql> show status like 'wsrep_cluster%';
+--------------------------+--------------------------------------+
| Variable_name | Value |
+--------------------------+--------------------------------------+
| wsrep_cluster_conf_id | 4 |
| wsrep_cluster_size | 2 |
| wsrep_cluster_state_uuid | 60bd9144-b9fb-11e8-b712-67f101a461c4 |
| wsrep_cluster_status | Primary |
+--------------------------+--------------------------------------+
4 rows in set (0.00 sec)
在202这个节点服务已经停止,集群只有2个节点的情况下,在201这个节点上创建数据库db1并创建表t1然后插入一些数据。
mysql> create database db1;
Query OK, 1 row affected (0.01 sec)
mysql> use db1;
Database changed
mysql> create table t1( id bigint auto_increment primary key ,name varchar (30) not null);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t1 (name) values ('A1'),('A2'),('A3');
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from t1;
+----+------+
| id | name |
+----+------+
| 2 | A1 |
| 4 | A2 |
| 6 | A3 |
+----+------+
3 rows in set (0.00 sec)
然后将202这个节点的mysql服务启动,如果成功,202这个节点将会自动加入集群中,同时进行数据的同步恢复工作。
本次测试中节点启动失败,错误信息如下:
2018-09-17T21:17:54.148724Z 2 [Note] WSREP: REPL Protocols: 8 (3, 2)
2018-09-17T21:17:54.148815Z 2 [Note] WSREP: New cluster view: global state: 60bd9144-b9fb-11e8-b712-67f101a461c4:19, view# 15: Primary, number of nodes: 3, my index: 0, protocol version 3
2018-09-17T21:17:54.148854Z 2 [Note] WSREP: Setting wsrep_ready to true
2018-09-17T21:17:54.148888Z 2 [Warning] WSREP: Gap in state sequence. Need state transfer.
2018-09-17T21:17:54.148922Z 2 [Note] WSREP: Setting wsrep_ready to false
2018-09-17T21:17:54.149489Z 0 [Note] WSREP: Initiating SST/IST transfer on JOINER side (wsrep_sst_xtrabackup-v2 --role 'joiner' --address '11.1.1.202' --datadir '/var/lib/mysql/' --defaults-file '/etc/my.cnf' --defaults-group-suffix '' --parent '2013' '' )
2018-09-17T21:17:54.746407Z WSREP_SST: [WARNING] Found a stale sst_in_progress file: /var/lib/mysql//sst_in_progress
2018-09-17T21:17:55.141227Z 2 [Note] WSREP: Prepared SST/IST request: xtrabackup-v2|11.1.1.202:4444/xtrabackup_sst//1
2018-09-17T21:17:55.141260Z 2 [Note] WSREP: Auto Increment Offset/Increment re-align with cluster membership change (Offset: 1 -> 1) (Increment: 1 -> 3)
2018-09-17T21:17:55.141280Z 2 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
2018-09-17T21:17:55.141310Z 2 [Note] WSREP: Assign initial position for certification: 19, protocol version: 3
2018-09-17T21:17:55.141368Z 0 [Note] WSREP: Service thread queue flushed.
2018-09-17T21:17:55.141429Z 2 [Note] WSREP: Check if state gap can be serviced using IST
2018-09-17T21:17:55.141449Z 2 [Note] WSREP: Local UUID: 00000000-0000-0000-0000-000000000000 != Group UUID: 60bd9144-b9fb-11e8-b712-67f101a461c4
2018-09-17T21:17:55.141508Z 2 [Note] WSREP: State gap can't be serviced using IST. Switching to SST
2018-09-17T21:17:55.141519Z 2 [Note] WSREP: Failed to prepare for incremental state transfer: Local state UUID (00000000-0000-0000-0000-000000000000) does not match group state UUID (60bd9144-b9fb-11e8-b712-67f101a461c4): 1 (Operation not permitted)
at galera/src/replicator_str.cpp:prepare_for_IST():538. IST will be unavailable.
2018-09-17T21:17:55.143251Z 0 [Note] WSREP: Member 0.0 (202.coe2coe.me) requested state transfer from '*any*'. Selected 1.0 (203.coe2coe.me)(SYNCED) as donor.
2018-09-17T21:17:55.143274Z 0 [Note] WSREP: Shifting PRIMARY -> JOINER (TO: 19)
2018-09-17T21:17:55.143320Z 2 [Note] WSREP: Requesting state transfer: success, donor: 1
2018-09-17T21:17:55.143344Z 2 [Note] WSREP: GCache history reset: 00000000-0000-0000-0000-000000000000:0 -> 60bd9144-b9fb-11e8-b712-67f101a461c4:19
2018-09-17T21:17:55.146832Z 0 [Warning] WSREP: 1.0 (203.coe2coe.me): State transfer to 0.0 (202.coe2coe.me) failed: -2 (No such file or directory)
2018-09-17T21:17:55.146861Z 0 [ERROR] WSREP: gcs/src/gcs_group.cpp:gcs_group_handle_join_msg():766: Will never receive state. Need to abort.
2018-09-17T21:17:55.146890Z 0 [Note] WSREP: gcomm: terminating thread
2018-09-17T21:17:55.146916Z 0 [Note] WSREP: gcomm: joining thread
2018-09-17T21:17:55.147014Z 0 [Note] WSREP: gcomm: closing backend
2018-09-17T21:17:55.148178Z 0 [Note] WSREP: Current view of cluster as seen by this node
在尝试删除grastate.dat和sst_in_progress文件后启动仍然失败的情况下,彻底删除了/var/lib/mysql目录的内容,然后使用innobackupex对一个现存节点制作了全备,然后在202这个节点上进行了恢复,最后修改了grastate.dat文件,结果这个节点再次启动后成功加入了集群中。
操作过程如下:
在203这个节点上制作全备。
[root@203 bak]# innodbbackupex --user=pxc --password=123456 --single-transaction .
压缩打包:
[root@203 bak]# tar -zcf pxc-db-bak.tar.gz 2018-09-18_06-04-51
拷贝到202节点上:
scp -P8801 [email protected]:/home/u/bak/pxc-db-bak.tar.gz
同时拷贝203的grastate.dat文件作为模板。
scp -P8801 [email protected]:/var/lib/mysql/grastate.dat .
在202上解压缩备份文件并进行恢复:
tar -zxf pxc-db-bak.tar.gz
innobackupex --apply-log ./2018-09-18_06-04-51/
[root@202 bak]# innobackupex --copy-back 2018-09-18_06-04-51/
查看innobackupex产生的xtrabackup_galera_info文件:
[root@202 bak]# cat 2018-09-18_06-04-51/xtrabackup_galera_info
60bd9144-b9fb-11e8-b712-67f101a461c4:19
最后将203上的grastate.dat文件修改为:
cat grastate.dat
# GALERA saved state
version: 2.1
uuid: 60bd9144-b9fb-11e8-b712-67f101a461c4
seqno: 19
safe_to_bootstrap: 0
执行mysqld_safe --user=mysql&
节点202上的mysql服务启动成功。
此时查看集群状态:
mysql> show status like 'wsrep_cluster_size';
+--------------------+-------+
| Variable_name | Value |
+--------------------+-------+
| wsrep_cluster_size | 3 |
+--------------------+-------+
1 row in set (0.00 sec)
mysql> show status like 'wsrep_incoming%';
+--------------------------+-------------------------------------------------+
| Variable_name | Value |
+--------------------------+-------------------------------------------------+
| wsrep_incoming_addresses | 11.1.1.202:3306,11.1.1.203:3306,11.1.1.201:3306 |
+--------------------------+-------------------------------------------------+
1 row in set (0.01 sec)
mysql> show status like 'wsrep_connected';
+-----------------+-------+
| Variable_name | Value |
+-----------------+-------+
| wsrep_connected | ON |
+-----------------+-------+
1 row in set (0.01 sec)
可以看到3个节点201,202,203均在集群当中。
在202节点上查询db1.t1表:
[root@202 bak]# mysql -upxc -p -h11.1.1.202 -e "select * from db1.t1;"
Enter password:
+----+------+
| id | name |
+----+------+
| 2 | A1 |
| 4 | A2 |
| 6 | A3 |
+----+------+
正确找到了之前在201节点上创建的表的数据,三个节点的数据已经完全同步了。