概述

Percona XtraDB Cluster(PXC),提供了多主数据同步复制,可以实现多个节点的数据同步复制以及读写,提供了高可用以及数据强一致性。

PXC的优点

  • 服务高可用,可多节点同时提供读写服务
  • 数据并行复制,几乎无延迟
  • 新节点加入群集自动配置并同步数据
  • 数据严格一致性
  • Percona版本完全兼容MySQL

PXC的缺点

  • 只支持InnoDB引擎
  • PXC要求严格一致性,事务可能存在回退的情况,例如两个节点同时执行同一变更,其中一个则会被回退
  • PXC要求并行写入所有节点,写入效率取决与最慢的节点,性能存在短板效应
  • 所有表都必须有主键
  • 不支持LOCK TABLE显示锁表操作

SST

State Snapshot Transfer(SST)是将所有数据从一个节点复制到另一个节点的过程。在新节点加入群集后初始化全量数据,通常使用SST。SST支持三种方式:mysqldump、rsync、xtrabackup,mysqldump和rsync在执行时会变成read-only复制

IST

Incremental State Transfer(IST)是将增量更改从一个节点复制到另一个节点。在SST同步时会产生新的变更,IST在节点中使用高速缓存机制来记录这部分数据,在SST结束之后再执行IST应用增量变更。
使用下列命令查看当前状态

SQL> SHOW STATUS LIKE ‘wsrep_local_state_comment’;

复制原理

  • 事务在本地节点执行时采取乐观策略,成功广播到所有节点后再做冲突检测
  • 检测到冲突时,本地事务优先回滚
  • 每个节点独立、异步执行队列中的write set
  • 事务在本地节点执行成功返回客户端后,其他节点保证该事务一定会被执行,因此可能存在延时

端口说明

端口 描述
4444 SST传输端口
4567 Cluster内部通信
4568 IST传输端口

资源清单
建议使用五台机器,三台做数据库群集节点,两台安装haproxy+keepalived,其中haproxy用于数据库请求的负载,keepalived用于vip故障切换,应用请求从vip连接到haproxy,haproxy再下发到数据库中。

Percona XtraDB安装

下载介质
Download Percona XtraDB Cluster

安装数据库
参考MySQL安装部署(一)进行三个节点数据库的安装

安装依赖

$ yum install socat perl-Digest-MD5 rsync -y

安装xtrabackup

$ rpm -ivh libev-4.15-3.el7.x86_64.rpm
$ rpm -ivh percona-xtrabackup-24-2.4.12-1.el7.x86_64.rpm

在my.cnf中加入PXC集群配置

#galera插件位置
wsrep_provider=/usr/lib64/galera3/libgalera_smm.so
#群集成员地址
wsrep_cluster_address=gcomm://10.240.204.157,10.240.204.165,10.240.204.149
#binlog格式
binlog_format=ROW
#默认存储引擎
default_storage_engine=InnoDB
#同步并发数,建议为CPU核数或1.5倍
wsrep_slave_threads= 8
wsrep_log_conflicts
#自增锁离散分配自增主键
innodb_autoinc_lock_mode=2
#当前成员地址
wsrep_node_address=10.240.204.157
#群集名称
wsrep_cluster_name=pxc-cluster
#当前成员名称
wsrep_node_name=node157
#群集全量同步方式
wsrep_sst_method=xtrabackup-v2
pxc_strict_mode=ENFORCING
wsrep_sst_auth="sst:Abcd123#"

Tips:三个节点的server-id,wsrep_node_name,wsrep_node_address参数不能一致

启动主节点

#yum安装
Centos6:/etc/init.d/mysql bootstrap-pxc
Centos7:systemctl start mysql@bootstrap.service
# 包安装
$ /usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf --wsrep-new-cluster --user=mysql &

创建同步用户

sql> GRANT RELOAD,LOCK TABLES,PROCESS,REPLICATION CLIENT ON *.* TO 'sst'@'%' IDENTIFIED BY 'Abcd123#';
sql> FLUSH PRIVILEGES;

查看群集状态

root@(none) 12:11:  show status like 'wsrep_cluster%';
+--------------------------+--------------------------------------+
| Variable_name | Value |
+--------------------------+--------------------------------------+
| wsrep_cluster_weight | 3 |
| wsrep_cluster_conf_id | 27 |
| wsrep_cluster_size | 3 |
| wsrep_cluster_state_uuid | e1ee81ca-2174-11e9-9f34-f6faa4b3eded |
| wsrep_cluster_status | Primary |
+--------------------------+--------------------------------------+

启动其它节点

$ /etc/init.d/mysqld start

PXC检查脚本配置

安装配置xinetd

$ yum install xinetd -y
$ echo 'mysqlchk 9200/tcp # mysqlchk' >> /etc/services
$ cp /usr/local/mysql/xinetd.d/mysqlchk /etc/xinetd.d/
$ echo "server = /usr/local/mysql/bin/clustercheck" >>/etc/xinetd.d/mysqlchk
$ systemctl enable xinetd
$ systemctl start xinetd

创建检查用户

sql> GRANT PROCESS ON *.* TO 'clustercheckuser'@'localhost' IDENTIFIED BY 'clustercheckpassword!';
sql> flush privileges;

执行clustercheck,确保返回200

$ clustercheck 
HTTP/1.1 200 OK
Content-Type: text/plain
Connection: close
Content-Length: 40
Percona XtraDB Cluster Node is synced

Haproxy安装配置

解压安装包

$ tar -xzvf haproxy-1.8.14.tar.gz
$ cd haproxy-1.8.14

编译安装

$ make PREFIX=/usr/local/haproxy TARGET=linux2628
$ make install PREFIX=/usr/local/haproxy

配置环境变量

$ echo “export PATH=/usr/local/haproxy/sbin:$PATH” >> /etc/profile
$ source /etc/profile

创建haproxy用户

$ groupadd haproxy
$ useradd -g haproxy haproxy

配置haproxy参数

global
log 127.0.0.1 local0
log 127.0.0.1 local1 notice
maxconn 12000
user haproxy
group haproxy
daemon

defaults
log global
mode tcp
option tcplog
option dontlognull
retries 3
maxconn 12000
option redispatch
timeout connect 10s
timeout client 30m
timeout server 30m

listen admin_stats
mode http
bind 0.0.0.0:80
stats uri /haproxy/stats
stats auth admin:admin

listen galera
mode tcp
bind 0.0.0.0:33006
balance leastconn
option tcpka
#option mysql-check user haproxy
server node137 10.0.8.137:33006 check port 9200 inter 12000 rise 3 fall 3
server node138 10.0.8.138:33006 check port 9200 inter 12000 rise 3 fall 3
server node139 10.0.8.139:33006 check port 9200 inter 12000 rise 3 fall 3

启动haproxy

$ haproxy –f /etc/haproxy/haproxy.cfg

Tips:haproxy提供了WEB可视化(http://ip/haproxy/stats),可以查看节点状态

Keepalived安装配置

安装keepalived

$ yum install -y keepalived

配置主节点参数

cat /etc/keepalived/keepalived.conf 
global_defs {
router_id mdmdb02
}
vrrp_script chk_pxc {
script "/etc/keepalived/chk_haproxy.sh"
interval 3
weight 3
}

vrrp_instance mdmdb {
virtual_router_id 41
state BACKUP
interface ens160 /* 网卡名称 */
nopreempt
priority 90
authentication {
auth_type PASS
auth_pass VANKE
}
track_script {
chk_pxc
}
virtual_ipaddress {
10.0.8.149 /* VIP */
}
}

配置从节点状态

$ cat /etc/keepalived/keepalived.conf 
global_defs {
router_id mdmdb02
}
vrrp_script chk_pxc {
script "/etc/keepalived/chk_haproxy.sh"
interval 3
weight 3
}

vrrp_instance mdmdb {
virtual_router_id 41
state BACKUP
interface ens160 /* 网卡名称 */
nopreempt
priority 90
authentication {
auth_type PASS
auth_pass VANKE
}
track_script {
chk_pxc
}
virtual_ipaddress {
10.0.8.149 /* VIP */
}
}

配置检查脚本

$ cat /etc/keepalived/chk_haproxy.sh 
#!/bin/bash
if [ `ps -C haproxy --no-header |wc -l` -eq 0 ] ; then
haproxy -f /etc/haproxy/haproxy.cfg
sleep 3
if [ `ps -C haproxy --no-header |wc -l` -eq 0 ] ; then
systemctl stop keepalived
fi
fi

启动keepalived

$ systemctl start keepalived.service

附录

[1]一个节点关闭

在出于维护配置或异常关闭的情况,群集运行过程中一个节点关闭,当它再次启动时会根据参数wsrep_cluster_address的设置再次加入群集,这时会从群集中选择一个Donor(捐献者),如果缓存(gcache.size)中还保留关闭期间的新事务操作,则可以直接通过IST加入群集,否则需要做SST全量复制,因此挑选一个最佳的Donor很重要。

[2]两个节点正常关闭

群集中两个节点都正常关闭的情况下,群集大小则为1,剩余节点单独作为群集提供服务。在将节点加回群集时,剩余节点会切换到Donor/Desynced状态,因为它要向第一个加入的节点提供状态转移。数据库已经可读写,但可能会比较慢,这取决你迁移状态的效率。

另外在第一个节点加入群集后,第二个在加入时需确认不会将第一个加入的节点作为捐献者,因为第一个节点可能不存在gcache,因此需要手动指定捐献者

service mysql start --wsrep_sst_donor=nodeC

[3]三个节点全部正常关闭

在所有节点关闭时,集群也就不存在了,需要重新初始化群集。在正常关闭的情况下PXC会将最后的postion记录在grastate.dat中,根据比较seqno,可以知道那个节点是最后关闭的节点。群集必须以该节点进行引导,重构群集。

#yum安装
Centos6:/etc/init.d/mysql bootstrap-pxc
Centos7:systemctl start mysql@bootstrap.service
# 包安装
$ /usr/local/mysql/bin/mysqld --defaults-file=/etc/my.cnf --wsrep-new-cluster --user=mysql &

其它节点启动后加入群集时,必须通过SST方式加入,因为重新启动时不会保留gcache缓存。因此建议在完全停止集群前停止数据写入,让所有节点都处于一个位置

[4]两个节点异常关闭

两个节点因为断电,硬件故障等情况导致的异常关闭,唯一存活的节点不单独形成仲裁,将退回到NON-PRIMARY模式,进程依旧存在,但无法提供读写服务\

mysql> select * from test.t1;
ERROR 1047 (08S01): Unknown command

这时可以通过下列方式手动让存活节点形成一个新的群集,但必须先确认其它两个节点处于关闭状态,而不是网络不可达等情况造成的,最后形成两个独立的群集。

sql> SET GLOBAL wsrep_provider_options='pc.bootstrap=true';

[5]所有节点都异常关闭

在极端情况下所有节点都异常关闭,grastate.dat未更新最后的信息,无法确认哪个节点是最后关闭的,也无法确认节点数据是否一致,因此需要先找到最后关闭的节点作为群集引导。在启动数据库前通过下列方式检查节点最后一个序列号

$ mysqld_safe --wsrep-recover
140821 15:57:15 mysqld_safe Logging to '/var/lib/mysql/percona3_error.log'.
140821 15:57:15 mysqld_safe Starting mysqld daemon with databases from /var/lib/mysql
140821 15:57:15 mysqld_safe WSREP: Running position recovery with --log_error='/var/lib/mysql/wsrep_recovery.6bUIqM' --pid-file='/var/lib/mysql/percona3-recover.pid'
140821 15:57:17 mysqld_safe WSREP: Recovered position 4b83bbe6-28bb-11e4-a885-4fc539d5eb6a:2
140821 15:57:19 mysqld_safe mysqld from pid file /var/lib/mysql/percona3.pid ended

上面的节点事务日志序号为2,依次查询找到最新的节点引导建立群集,再启动其它节点加入群集

但在PXC 5.6.19开始不再需要上述步骤,有一个新选项pc.recovery,它将群集状态保存到每个节点的gvwstate.dat,文件形式如下

$ cat /var/lib/mysql/gvwstate.dat
my_uuid: 76de8ad9-2aac-11e4-8089-d27fd06893b9
#vwbeg
view_id: 3 6c821ecc-2aac-11e4-85a5-56fe513c651f 3
bootstrap: 0
member: 6c821ecc-2aac-11e4-85a5-56fe513c651f 0
member: 6d80ec1b-2aac-11e4-8d1e-b2b2f6caf018 0
member: 76de8ad9-2aac-11e4-8089-d27fd06893b9 0
#vwend

节点将在启动时读取最后一个状态,并在所有成员再次开始相互见面后尝试还原。这使得PXC群集无需任何手动干预即可自动从关机中恢复

140823 15:28:55 [Note] WSREP: restore pc from disk successfully
(...)
140823 15:29:59 [Note] WSREP: declaring 6c821ecc at tcp://192.168.90.3:4567 stable
140823 15:29:59 [Note] WSREP: declaring 6d80ec1b at tcp://192.168.90.4:4567 stable
140823 15:29:59 [Warning] WSREP: no nodes coming from prim view, prim not possible
140823 15:29:59 [Note] WSREP: New COMPONENT: primary = no, bootstrap = no, my_idx = 2, memb_num = 3
140823 15:29:59 [Note] WSREP: Flow-control interval: [28, 28]
140823 15:29:59 [Note] WSREP: Received NON-PRIMARY.
140823 15:29:59 [Note] WSREP: New cluster view: global state: 4b83bbe6-28bb-11e4-a885-4fc539d5eb6a:11, view# -1: non-Primary, number of nodes: 3, my index: 2, protocol version -1
140823 15:29:59 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
140823 15:29:59 [Note] WSREP: promote to primary component
140823 15:29:59 [Note] WSREP: save pc into disk
140823 15:29:59 [Note] WSREP: New COMPONENT: primary = yes, bootstrap = yes, my_idx = 2, memb_num = 3
140823 15:29:59 [Note] WSREP: STATE EXCHANGE: Waiting for state UUID.
140823 15:29:59 [Note] WSREP: clear restored view
(...)
140823 15:29:59 [Note] WSREP: Bootstrapped primary 00000000-0000-0000-0000-000000000000 found: 3.
140823 15:29:59 [Note] WSREP: Quorum results:
version = 3,
component = PRIMARY,
conf_id = -1,
members = 3/3 (joined/total),
act_id = 11,
last_appl. = -1,
protocols = 0/6/2 (gcs/repl/appl),
group UUID = 4b83bbe6-28bb-11e4-a885-4fc539d5eb6a
140823 15:29:59 [Note] WSREP: Flow-control interval: [28, 28]
140823 15:29:59 [Note] WSREP: Restored state OPEN -> JOINED (11)
140823 15:29:59 [Note] WSREP: New cluster view: global state: 4b83bbe6-28bb-11e4-a885-4fc539d5eb6a:11, view# 0: Primary, number of nodes: 3, my index: 2, protocol version 2
140823 15:29:59 [Note] WSREP: wsrep_notify_cmd is not defined, skipping notification.
140823 15:29:59 [Note] WSREP: REPL Protocols: 6 (3, 2)
140823 15:29:59 [Note] WSREP: Service thread queue flushed.
140823 15:29:59 [Note] WSREP: Assign initial position for certification: 11, protocol version: 3
140823 15:29:59 [Note] WSREP: Service thread queue flushed.
140823 15:29:59 [Note] WSREP: Member 1.0 (percona3) synced with group.
140823 15:29:59 [Note] WSREP: Member 2.0 (percona1) synced with group.
140823 15:29:59 [Note] WSREP: Shifting JOINED -> SYNCED (TO: 11)
140823 15:29:59 [Note] WSREP: Synchronized with group, ready for connections

[6]脑裂
假设群集是由偶数节点构成的,分2个机房,每个机房三台,当两个机房的网络中断则出现脑裂,整个集群停止服务请求。为了避免这种情况在数据节点为偶数时添加仲裁器或者设置某些节点的权重,也可以在发生后手动指定形成新的群集
fissures

sql> SET GLOBAL wsrep_provider_options='pc.bootstrap=true';

参考链接

  1. how to recover a PXC cluster
  2. Percona XtraDB Cluster 5.7 Documentation