搭建高可用mysql集群PXC

1 常见mysql集群

常见mysql集群方案有replication和PXC,两种方案比较:

常见mysql集群方案

两种集群方案比较


replication和PXC两种方案写入数据同步比较:

  • replication采用异步复制,无法保证数据的一致。
  • PXC采用同步复制,事务在集群的所有节点要么同时提交,要么不提交,PXC使用的是percona,percona是mysql改进版,性能挺升很大。

两种集群写入比较


2 单机版mysql集群PXC

2.1 在docker上搭建mysql集群PXC

docker hub地址: https://hub.docker.com/r/percona/percona-xtradb-cluster/

下面搭建3个节点的mysql集群:

# 下载镜像
docker pull percona/percona-xtradb-cluster

# 修改镜像名
docker tag docker.io/percona/percona-xtradb-cluster pxc

# 处于安全考虑,为集群创建一个内部网络,外面是无法直接访问的
docker network create pxc-net
   # 查看内部网络pxc-net详细信息
docker inspect pxc-net

# 由于pxc不能直接使用映射目录来启动,所以要创建docker数据卷,因为有三个节点,分别创建三个数据卷
docker volume create --name mysql-data-node1
docker volume create --name mysql-data-node2
docker volume create --name mysql-data-node3
# 查看数据卷信息,可以查看数据存放在本地的路径
docker inspect <数据卷名称>

# 创建mysql集群容器节点
# 创建各个mysql节点时需要注意,最好先等第一个节点启动完成才创建第二或第三节点,如果第一节点的mysql还没初始化完成就创建第二或第三的话,有可能会造成同步数据失败。怎么才确认第一个节点的mysql初始化完毕呢?使用mysql客户端连接,如果能正常连接操作,说明第一个节点初始化完毕。

# 创建第一个节点
docker run -d --net=pxc-net -e MYSQL_ROOT_PASSWORD=123456 -e CLUSTER_NAME=PXC -e XTRABACKUP_PASSWORD=123456 --privileged \
-p 3306:3306 \
-v mysql-data-node1:/var/lib/mysql \
--name=mysql-node1 \
pxc

# 创建第二个节点
docker run -d --net=pxc-net -e MYSQL_ROOT_PASSWORD=123456 -e CLUSTER_NAME=PXC -e XTRABACKUP_PASSWORD=123456 --privileged \
-p 3307:3306 \
-v mysql-data-node2:/var/lib/mysql \
--name=mysql-node2 \
-e CLUSTER_JOIN=mysql-node1 \
pxc

# 创建第三个节点
docker run -d --net=pxc-net -e MYSQL_ROOT_PASSWORD=123456 -e CLUSTER_NAME=PXC -e XTRABACKUP_PASSWORD=123456 --privileged \
-p 3308:3306 \
-v mysql-data-node3:/var/lib/mysql \
--name=mysql-node3 \
-e CLUSTER_JOIN=mysql-node1 \
pxc

# 使用mysql客户端连接一个节点,修改数据,如果其他节点数据同步成功,说明mysql集群搭建完成。


把上面docker命令写在当前目录的docker-compose.yml文件里,配置内容如下:

version: '3'

services:

  mysql-node1:
    image: percona/percona-xtradb-cluster
    privileged: true
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      XTRABACKUP_PASSWORD: 123456
      CLUSTER_NAME: PXC
    ports:
      - 10001:3306
    volumes:
      - mysql-data-node1:/var/lib/mysql
    networks:
      - pxc-net

  mysql-node2:
    image: percona/percona-xtradb-cluster
    privileged: true
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      XTRABACKUP_PASSWORD: 123456
      CLUSTER_NAME: PXC
      CLUSTER_JOIN: mysql-node1
    ports:
      - 10002:3306
    volumes:
      - mysql-data-node2:/var/lib/mysql
    networks:
      - pxc-net


  mysql-node3:
    image: percona/percona-xtradb-cluster
    privileged: true
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      XTRABACKUP_PASSWORD: 123456
      CLUSTER_NAME: PXC
      CLUSTER_JOIN: mysql-node1
    ports:
      - 10003:3306
    volumes:
      - mysql-data-node3:/var/lib/mysql
    networks:
      - pxc-net

volumes:
  mysql-data-node1:
  mysql-data-node2:
  mysql-data-node3:

networks:
  pxc-net:
      driver: bridge


如果有节点出现故障,查看日志提示错误信息: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 . 无法从当前节点实现安全引导,原因是当前节点不是集群中最后离开的节点,也就是说当前节点可能未能包含所有的更新,解决办法:

# 找出对应节点的grastate.dat文件位置
find / -name grastate.dat

# 例如:
vim /var/lib/docker/volumes/proxy_mysql-data-node1/_data/grastate.dat
# 然后把safe_to_bootstrap的值置为1


2.2 添加负载均衡

mysql集群创建好了,需要一个负载均衡工具把请求均匀分摊到各个节点上,这里使用haproxy负载均衡工具。

下载haproxy镜像

docker pull haproxy

因为官方的镜像默认没有配置文件,也没有启动haproxy程序,需要打包一个默认启动haproxy程序的镜像,

global
    #工作目录
    chroot /usr/local/etc/haproxy
    #日志文件,使用rsyslog服务中local5日志设备(/var/log/local5),等级info
    log 127.0.0.1 local5 info

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:6688
    #访问协议
    mode        http
    #URI相对地址
    stats uri   /dbs
    #统计报告格式
    stats realm     Global\ statistics
    #登陆帐户信息
    stats auth  admin:123456

#数据库负载均衡
listen  proxy-mysql
    #访问的IP和端口
    bind  0.0.0.0:3306  
    #网络协议
    mode  tcp
    #负载均衡算法(轮询算法)
    #轮询算法:roundrobin
    #权重算法:static-rr
    #最少连接算法:leastconn
    #请求源IP算法:source
    balance  roundrobin
    #日志格式
    option  tcplog
    #在MySQL中创建一个没有权限的haproxy用户,密码为空。Haproxy使用这个账户对MySQL数据库心跳检测
    option  mysql-check user haproxy
    # 这里使用节点名称代替ip地址
    server  MySQL_1 mysql-node1:3306 check weight 1 maxconn 2000
    server  MySQL_2 mysql-node2:3306 check weight 1 maxconn 2000
    server  MySQL_3 mysql-node3:3306 check weight 1 maxconn 2000
    #使用keepalive检测死链
    option  tcpka


在当前目录下创建一个Dockerfile文件,内容如下:

FROM haproxy
RUN mkdir -p /usr/local/etc/haproxy
CMD haproxy -f /usr/local/etc/haproxy/haproxy.cfg

打包新镜像haproxy-run

docker build -t haproxy-run .


因为haproxy需要登录每个节点的mysql来检测心跳,使用mysql客户端登陆集群中的一个节点,创建一个新用户haproxy,这个用户无任何权限,也无需密码登录。只需创建一次,这个用户会自动同步到其他节点。

create user ‘haproxy’@‘%’ identified by “;


编辑容器管理配置文件docker-compose.yml,新添加容器haproxy1,内容如下:

  haproxy1:
    image: haproxy-run
    privileged: true
    ports:
      - 7001:3306
      - 7101:6688
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
    networks:
      - pxc-net

启动容器

docker-compose start haproxy1

在浏览器登陆haproxy后台监控页面,查看mysql集群状态,其中后台监听端口、登陆的用户和密码都在配置文件haproxy.cfg。例如在浏览器打开http://192.168.8.200:7101/dbs 查看集群状态。

验证是否可以使用mysql客户端通过haproxy登陆,填写节点mysql的用户名和密码,如果可以登陆,说明可以通过haproxy对集群实现负载均衡。


2.3 实现高可用负载均衡器

单点负载均衡器的缺点是当负载均衡器出现故障时,整个系统就不能工作了,需要多个作为备用才能实现高可用。

重新打包haproxy镜像,在新镜像安装和配置keepalived,keepalived是基于vrrp协议的一款高可用软件,修改当前目录Dockerfile文件,内容如下:

FROM haproxy

# 安装keepalived
RUN apt-get -y update
RUN apt-get -y install keepalived
RUN mkdir -p /usr/local/etc/haproxy

# 启动keepalived和haproxy
CMD keepalived -f /etc/keepalived/keepalived.conf && haproxy -f /usr/local/etc/haproxy/haproxy.cfg

打包镜像,镜像名为haproxy-keepalived。

docker build -t haproxy-keepalived .


在当前目录新建keepalived的配置文件keepalived.conf,内容如下:

vrrp_instance  VI_1 {
    state  MASTER
    interface  eth0
    virtual_router_id  51
    priority  100
    advert_int  1
    authentication {
        auth_type  PASS
        auth_pass  123456
    }
    virtual_ipaddress {
        172.20.0.250
    }
}

配置注意事项:

  • 使用docker映射配置文件时,keepalived.conf配置文件的权限是为644(root权限),如果文件权限有误,启动keepalived会报错Configuration file ‘/etc/keepalived/keepalived.conf’ is not a regular non-executable file。
  • 配置文件里字段virtual_ipaddress填写的ip是容器所在网段中没有使用的ip地址。


编辑容器管理配置文件docker-compose.yml,把原来的容器haproxy1删除,新添加两个容器haproxy1和haproxy2,当然可以添加更多备用负载均衡器,新添加内容如下:

haproxy1:
    # 由于haproxy启动时不能预先解析mysql集群节点名称对应的ip,需要预先打包新镜像,然后使用该镜像。
    image: haproxy-keepalived
    privileged: true
    ports:
      # 3306是mysql集群容器里使用的端口
      - 7001:3306
      # haproxy后台监控端口
      - 7101:6688
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
      - ./keepalived.conf:/etc/keepalived/keepalived.conf
    networks:
      - pxc-net

  haproxy2:
    image: haproxy-keepalived
    privileged: true
    ports:
      - 7002:3306
      - 7102:6688
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
      - ./keepalived.conf:/etc/keepalived/keepalived.conf
    networks:
      - pxc-net

启动容器haproxy1和haproxy2

docker-compose restart haproxy1 haproxy2


因为和haproxy1和haproxy2使用的keepalived.conf配置一样,没有明确指定哪个是主负载均衡器,哪个先抢到虚拟ip,哪个就为主,则另一个为备用,要想知道那个为主负载均衡器,分别进入容器haproxy1和haproxy2,使用命令ip a查看,如果当前容器有绑定keepalived.conf配置中指定的虚拟ip地址,说明当前容器为主负载均衡器,其他为备用负载均衡器,

检验负载均衡器是否能够高可用很简单,停止主负载均衡器,然后使用ping 172.20.0.250(配置中指定的虚拟ip),如果能ping通,说明备用负载均衡器在起作用。


2.4 实现外部访问mysql集群PXC

现在问题是虚拟ip属于容器内网,只能在本机访问,为了能够使外面访问,需要在宿主机也安装keepalived,把宿主机指定的虚拟ip指向负载均衡器的虚拟ip。

宿主机安装keepalived:

yum install -y keepalived

配置keepalived.conf文件:

vim /etc/keepalived/keepalived.conf

注意字段interface,这是宿主机地址驱动名,不同宿主机的名称可能不一样,virtual_ipaddress字段直接填写本机ip地址,其中6688是haproxy后台监控端口,3306是集群中mysql端口,配置内容如下:

vrrp_instance VI_1 {
    state MASTER
    interface eno33554984
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 123456
    }
    virtual_ipaddress {
        192.168.8.200
    }
}

virtual_server 192.168.8.200 6688 {
    delay_loop 3
    lb_algo rr
    lb_kind NAT
    persistence_timeout 50
    protocol TCP

    real_server 172.20.0.250 6688 {
        weight 1
    }
}

virtual_server 192.168.8.200 3306 {
    delay_loop 3
    lb_algo rr
    lb_kind NAT
    persistence_timeout 50
    protocol TCP

    real_server 172.20.0.250 3306 {
        weight 1
    }
}

启动keepalived

keepalived -f /etc/keepalived/keepalived.conf

然后使用mysql客户端测试是否可以连接mysql集群

mysql -h 192.168.8.200 -u root -p 123456

如果可以连接,说明单机版的高可用的mysql集群搭建完成。

以下是搭建高可用的mysql集群配置文件内容:

2.5 实现高可用mysql集群PXC的文件内容

mysql-cluster目录下文件列表:

.
├── docker-compose.yml
├── Dockerfile
├── haproxy.cfg
├── keepalived.conf
└── local-keepalived.conf

# 其中keepalived.conf是容器的keepalived配置文件,而local-keepalived.conf是宿主机keepalived配置文件。

使用:

# 打包镜像
docker build -t haproxy-keepalived .

# 启动mysql集群
docker-compose up -d

# 登录集群中的一个节点的mysql创建一个haproxy用户
mysql -h 192.168.8.200 -u root -p 123456

# 启动宿主机的keepalived
keepalived -f /etc/keepalived/keepalived.conf


(1) haproxy配置文件haproxy.cfg

global
    #工作目录
    chroot /usr/local/etc/haproxy
    #日志文件,使用rsyslog服务中local5日志设备(/var/log/local5),等级info
    log 127.0.0.1 local5 info

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:6688
    #访问协议
    mode        http
    #URI相对地址
    stats uri   /dbs
    #统计报告格式
    stats realm     Global\ statistics
    #登陆帐户信息
    stats auth  admin:123456
#数据库负载均衡
listen  proxy-mysql
    #访问的IP和端口
    bind  0.0.0.0:3306  
    #网络协议
    mode  tcp
    #负载均衡算法(轮询算法)
    #轮询算法:roundrobin
    #权重算法:static-rr
    #最少连接算法:leastconn
    #请求源IP算法:source 
    balance  roundrobin
    #日志格式
    option  tcplog
    #在MySQL中创建一个没有权限的haproxy用户,密码为空。Haproxy使用这个账户对MySQL数据库心跳检测
    option  mysql-check user haproxy
    server  MySQL_1 mysql-node1:3306 check weight 1 maxconn 2000  
    server  MySQL_2 mysql-node2:3306 check weight 1 maxconn 2000  
    server  MySQL_3 mysql-node3:3306 check weight 1 maxconn 2000 
    #使用keepalive检测死链
    option  tcpka  


(2) keepalived配置文件keepalived.conf

注意文件的权限和虚拟ip地址。

vrrp_instance marster {
    state  MASTER
    interface  eth0
    virtual_router_id  51
    priority  100
    advert_int  1
    authentication {
        auth_type  PASS
        auth_pass  123456
    }
    virtual_ipaddress {
        172.20.0.250
    }
}


(3) Dockerfile文件

FROM haproxy

# 安装keepalived
RUN apt-get -y update
RUN apt-get -y install keepalived
RUN mkdir -p /usr/local/etc/haproxy

# 启动keepalived和haproxy
CMD keepalived -f /etc/keepalived/keepalived.conf && haproxy -f /usr/local/etc/haproxy/haproxy.cfg


(4) docker-compose.yml文件

version: '3'

services:

  mysql-node1:
    image: percona/percona-xtradb-cluster
    privileged: true
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      XTRABACKUP_PASSWORD: 123456
      CLUSTER_NAME: PXC
    ports:
      - 10001:3306
    volumes:
      - mysql-data-node1:/var/lib/mysql
      # 备份数据
      - mysql-data-backup:/data
    networks:
      - pxc-net

  mysql-node2:
    image: percona/percona-xtradb-cluster
    privileged: true
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      XTRABACKUP_PASSWORD: 123456
      CLUSTER_NAME: PXC
      CLUSTER_JOIN: mysql-node1
    ports:
      - 10002:3306
    volumes:
      - mysql-data-node2:/var/lib/mysql
    networks:
      - pxc-net

  mysql-node3:
    image: percona/percona-xtradb-cluster
    privileged: true
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      XTRABACKUP_PASSWORD: 123456
      CLUSTER_NAME: PXC
      CLUSTER_JOIN: mysql-node1
    ports:
      - 10003:3306
    volumes:
      - mysql-data-node3:/var/lib/mysql
    networks:
      - pxc-net

  haproxy1:
    # 由于haproxy启动时不能预先解析mysql集群节点名称对应的ip,需要预先打包新镜像,然后使用该镜像。
    image: haproxy-keepalived
    privileged: true
    ports:
      # 3306是mysql集群容器里使用的端口
      - 7001:3306
      # haproxy后台监控端口
      - 7101:6688
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
      - ./keepalived.conf:/etc/keepalived/keepalived.conf
    networks:
      - pxc-net

  haproxy2:
    image: haproxy-keepalived
    privileged: true
    ports:
      - 7002:3306
      - 7102:6688
    volumes:
      - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg
      - ./keepalived.conf:/etc/keepalived/keepalived.conf
    networks:
      - pxc-net

# 创建容器数据卷
volumes:
  # 备份数据卷
  mysql-data-backup:
  mysql-data-node1:
  mysql-data-node2:
  mysql-data-node3:

# 创建的网络
networks:
  pxc-net:
      driver: bridge


(5) 宿主机的keepalived配置文件keepalived.conf

注意字段interface、virtual_ipaddress、real_server

vrrp_instance VI_1 {
    state MASTER
    interface eno33554984
    virtual_router_id 51
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 123456
    }
    virtual_ipaddress {
        192.168.8.200
    }
}


virtual_server 192.168.8.200 6688 {
    delay_loop 3
    lb_algo rr 
    lb_kind NAT
    persistence_timeout 50
    protocol TCP

    real_server 172.20.0.250 6688 {
        weight 1
    }
}


virtual_server 192.168.8.200 3306 {
    delay_loop 3
    lb_algo rr 
    lb_kind NAT
    persistence_timeout 50
    protocol TCP

    real_server 172.20.0.250 3306 {
        weight 1
    }
}


2.6 故障排查步骤

执行docker-compose up -d之后,如果出现故障,检查顺序:

# (1) 查看所有容器是否都启动完成
docker ps

# (2) 所有容器都启动完成,使用mysql客户端看是否能够连接mysql
mysql -h <宿主机ip> -u root -P 10001 -p <mysql密码>

# (3) 所有mysql节点都可以连接,在浏览器查看负载均衡器后台监控状态,http://<宿主机ip>:7101/dbs
#如果现实mysql节点都是红色,即心跳失败,说明mysql没有创建haproxy帐号,这个帐号专门用来检测mysql心跳的,在任意一个mysql节点创建haproxy帐号:
create user 'haproxy'@'%' identified by '';

# (4) 检查keepalived配置指定的虚拟ip是否和haproxy所在容器是否在同一网段,如果不同网段,是无法组成高可用的,需要修改映射出来的keepalived.conf配置,把虚拟ip修改成haproxy所在容器ip网段一致,然后重启容器haproxy1、haproxy2。
docker-compose restart haproxy1 haproxy2

# (5) 检查宿主机上的keepalived.conf配置是否填写是否正确,例如网络接口名称interface、虚拟ip地址virtual_ipaddress、转发的真实ip地址和端口real_server。

3 热备份数据

3.1 冷热备份介绍

冷备份

  • 冷备份是关闭数据库时候的备份方式,通常做法是拷贝数据文件替换,不是使用mysqldump导出。
  • 冷备份是最简单最安全的一种备份方式。
  • 大型网站无法做到关闭业务备份数据,所以冷备份不是最佳选择。
  • 如果线上的是mysql集群,可以停止集群中的某个节点,对该节点备份数据。


热备份

  • 热备份是在系统运行的状态下备份数据,也是难度最大的备份。
  • MySQL常见的热备份有LVM和Xtra Backup两种方案。
  • LVM备份时会锁表,只能读,不能写,会影响到业务,Xtrabackup热备份不影响业务。


Xtrabackup是一款基于InnoDB的在线热备开源工具,占用磁盘空间小,能够快速备份和恢复在线mysql数据,Xtrabackup优点:

  • 备份过程不锁表、快速可靠。
  • 备份过程不会打断正在执行的事务。
  • 能够基于压缩等功能节约磁盘空间和流量。

Xtrabackup备份有全量备份和增量备份,第一次备份使用全量备份,之后都是用增量备份。实际中是一天一次增量备份,一个星期全量备份一次。


3.2 Xtrabackup备份

搭建集群的时候已经在节点mysql-node1上映射了数据卷mysql-data-backup,然后进入节点mysql-node1安装Xtrabackup备份工具。

# 安装
apt-get -y update
apt-get -y install percona-xtrabackup-24

# 第一次进行全量备份
innobackupex --user=root --password=123456 /data/backup/full


3.3 数据还原

数据还原只有冷还原,没有热还原,PXC集群数据还原比较麻烦,具体步骤如下:

# 先创建一个新的mysql节点,节点已经映射到待还原的容器数据卷mysql-data-backup,然后进入容器,如果没有安装Xtrabackup的话,要预先安装
docker-compose exec mysql-node1 bash

# 删除默认的数据库数据
rm -rf /var/lib/mysql/*

# 回滚未提交的事务数据
innobackupex --user=root --password=123456 --apply-back  /data/backup/full/2018-05-05_10-10-11

# 还原数据
innobackupex --user=root --password=123456 --copy-back  /data/backup/full/2018-05-05_10-10-11

# 重启mysql节点,使数据生效
docker-compose restart mysql-node1


专题「数据库」的其它文章 »