为什么要搭MySQL集群

技术层面上,传统的单节点数据库,万一宕机了,就凉凉了。容灾性能差、抗并发能力有限,数据量大的时候查询有瓶颈。学习层面上,作为一个技术人了解一些技术相关的知识那也是无可厚非,爱折腾嘛。所以、本着“不把鸡蛋放在一个篮子里”的思想,我们来一起探讨学习下如何搭建MySQL集群。

MySQL集群的解决方案

关于搭建MySQL集群解决方案的操作方面,这部分知识其实是很死板的,没有特别多的含金量,真正有含金量的是挖掘其背后实现的原理和思路,并能够晓之以情动之以理地讲出来。这里主要介绍两种解决方案,我们抓牢它们的侧重点总结下吧。

PXC

  • 强一致性
  • 速度慢
  • 只支持InnoDB存储引擎同步
  • 所有节点可读写
  • 同步机制为同步
  • 存储信息价值量高

Replication

  • 弱一致性

  • 速度快

  • 从节点不可写入

  • 同步机制为异步

  • 存储信息价值量不高

这里我们主要讲下PXC,当你在一个节点进行相关操作,比如说插入一条数据、它会同步到其他节点,若所有节点同步成功则插入成功、若所有节点同步失败,则回滚并告知插入失败,这个我们后面实践一下就知道了。

创建MySQL集群的步骤(PXC)

(一)、拉取镜像并重命名

# 拉取镜像
docker pull percona/percona-xtradb-cluster # 重名名(这步也可以不做,我就是想后面少打点字)
docker tag percona/percona-xtradb-cluster:latest pxc:latest

(二)、组网

2.1、创建网络:

命令:docker network create mysql_net

演示:

 root@ataola  ~  docker network create mysql_net
79885a1c3b8a783e096a1610df08e353641bda74d0b996b4200f2ea5db3c5dbd
root@ataola  ~ 

2.2、查看网络:

命令:docker network inspect mysql_net

演示:

 root@ataola  ~  docker network inspect mysql_net
[
{
"Name": "mysql_net",
"Id": "79885a1c3b8a783e096a1610df08e353641bda74d0b996b4200f2ea5db3c5dbd",
"Created": "2020-05-30T21:26:53.852825919+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.19.0.0/16",
"Gateway": "172.19.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
root@ataola  ~ 

(三)、创建数据卷

3.1、创建数据卷

命令:docker volume create mysql_v

演示:

  root@ataola  ~  docker volume create mysql_v1
mysql_v1
root@ataola  ~  docker volume create mysql_v2
mysql_v2
root@ataola  ~  docker volume create mysql_v3
mysql_v3
root@ataola  ~  docker volume create mysql_v4
mysql_v4
root@ataola  ~  docker volume create mysql_v5
mysql_v5
root@ataola  ~ 

3.2:查看数据卷

命令:docker volume inspect mysql_v

演示:

✘  root@ataola  /home/caocao  docker volume inspect mysql_v1
[
{
"Driver": "local",
"Labels": {},
"Mountpoint": "/var/lib/docker/volumes/mysql_v1/_data",
"Name": "mysql_v1",
"Options": {},
"Scope": "local"
}
]
root@ataola  /home/caocao 

(四)、创建MySQL节点

4.1、主节点:

命令:docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=studypxc -e CLUSTER_NAME=PXC -e XTRABACKUP_PASSWORD=studymysql -v mysql_v1:/var/lib/mysql --privileged --name=mysql_node1 --net=mysql_net pxc

演示:

 root@ataola  ~  docker run -d -p 3306:3306 -e MYSQL_ROOT_PASSWORD=studypxc -e CLUSTER_NAME=PXC -e XTRABACKUP_PASSWORD=studymysql -v mysql_v1:/var/lib/mysql --privileged --name=mysql_node1 --net=mysql_net pxc
09e7715f71b4411974d862a2aa74ed0b1018fc4efb4196707576f935e1425f6b
root@ataola  ~  docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
09e7715f71b4 pxc "/entrypoint.sh my..." 8 seconds ago Up 6 seconds 0.0.0.0:3306->3306/tcp, 4567-4568/tcp mysql_node1
root@ataola  ~ 

4.2、从节点:

命令:docker run -d -p 3307:3306 -e MYSQL_ROOT_PASSWORD=studypxc -e CLUSTER_NAME=PXC -e XTRABACKUP_PASSWORD=studymysql -e CLUSTER_JOIN=mysql_node1 -v mysql_v2:/var/lib/mysql --privileged --name=mysql_node2 --net=mysql_net pxc

更改相关参数重复三次楼上操作,我们构建一个主节点外加四个从节点的mysql集群,这个时候我们执行docker ps -a看下,可以看到它们都跑起来了。

 root@ataola  ~  docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8d809616ea08 pxc "/entrypoint.sh my..." 9 seconds ago Up 8 seconds 4567-4568/tcp, 0.0.0.0:3310->3306/tcp mysql_node5
6dc023253205 pxc "/entrypoint.sh my..." 18 seconds ago Up 17 seconds 4567-4568/tcp, 0.0.0.0:3309->3306/tcp mysql_node4
18c232855938 pxc "/entrypoint.sh my..." 53 seconds ago Up 52 seconds 4567-4568/tcp, 0.0.0.0:3308->3306/tcp mysql_node3
6de60216270a pxc "/entrypoint.sh my..." 2 minutes ago Up 2 minutes 4567-4568/tcp, 0.0.0.0:3307->3306/tcp mysql_node2
09e7715f71b4 pxc "/entrypoint.sh my..." 10 minutes ago Up 10 minutes 0.0.0.0:3306->3306/tcp, 4567-4568/tcp mysql_node1
root@ataola  ~ 

然后我们可以通过docker network inspect mysql_net查看我们刚才创建集群的网络信息

  root@ataola  /home/caocao  docker network inspect mysql_net
[
{
"Name": "mysql_net",
"Id": "79885a1c3b8a783e096a1610df08e353641bda74d0b996b4200f2ea5db3c5dbd",
"Created": "2020-05-30T21:26:53.852825919+08:00",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.19.0.0/16",
"Gateway": "172.19.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Containers": {
"09e7715f71b4411974d862a2aa74ed0b1018fc4efb4196707576f935e1425f6b": {
"Name": "mysql_node1",
"EndpointID": "621bc0ec6e4adb78e72f0f78ca04d154029abe1f0bb91fc5ccd42156bfa32d52",
"MacAddress": "02:42:ac:13:00:02",
"IPv4Address": "172.19.0.2/16",
"IPv6Address": ""
},
"0e4ef69c19b981163067ec8af0c38b6fa84c380855c22309a884f0a0ed691912": {
"Name": "haproxy1",
"EndpointID": "02e8c179fdb7cbc4b3e7d72f8412129da487c65a3897e70e0da5693aa0d8d500",
"MacAddress": "02:42:ac:13:00:07",
"IPv4Address": "172.19.0.7/16",
"IPv6Address": ""
},
"18c232855938d7263b90a4ab88874676110c6197a601aa72d8bec8f52e73dd02": {
"Name": "mysql_node3",
"EndpointID": "010538caec3ab7aafe1c708ebebdffa43e268d68147cf4a60f86a77247b3ef86",
"MacAddress": "02:42:ac:13:00:04",
"IPv4Address": "172.19.0.4/16",
"IPv6Address": ""
},
"6dc0232532057a65db0607c92517307ee480bec32cb6311d6615555482670c7a": {
"Name": "mysql_node4",
"EndpointID": "cc59fe8fc8d7d02f367ec75b1897c51bf3731dbc5dc8d70510dde34aee6d4afa",
"MacAddress": "02:42:ac:13:00:05",
"IPv4Address": "172.19.0.5/16",
"IPv6Address": ""
},
"6de60216270a3b6f5bfbe5d94d40fa2449e443eb77b67d4bf005061dd4ff412e": {
"Name": "mysql_node2",
"EndpointID": "b674d25e3e6c999324de9704419b1c97f008ca6e5d27e825bc0fc61d600848ff",
"MacAddress": "02:42:ac:13:00:03",
"IPv4Address": "172.19.0.3/16",
"IPv6Address": ""
},
"8d809616ea08d46a3febb95259d9d0672d6112dd8cbe6c29f03f49cbc44ef444": {
"Name": "mysql_node5",
"EndpointID": "01f0988019206e959a7099736d9995cec1676aaec360db9bd78fa61b68a87f71",
"MacAddress": "02:42:ac:13:00:06",
"IPv4Address": "172.19.0.6/16",
"IPv6Address": ""
}
},
"Options": {},
"Labels": {}
}
]
root@ataola  /home/caocao 

至此,基于PXC解决方案MySQL集群搭建已经完成了,当然这里涉及到的一些命令和参数具体的还是要读者去看楼下参考文献的官方文档的。

负载均衡(haproxy)

在楼上的例子中,我们创建了一个MySQL集群,我们可以把它理解成一家超市。然后每个节点就是收银台。这个时候就会有个问题了,假设那批去超市购物的人不太正常了,买完东西都挤到1号收银台,那么1号收银台的收银员她承受的压力就会比较大,就会很焦虑。这个时候呢,超市经理拿这个大喇叭过来啊,你你你,去2号收银台、你们去3号收银台,购物的人很快地就付完钱回家了。haproxy就相当于这个超市经理,哪里有空闲就调度往哪个节点去。

在理解完haproxy做的事情后,我们来实践一番吧。

(一)、拉取镜像

这里我就不改名了,有兴趣的童鞋自己对照pxc改个名字。

docker pull haproxy

(二)、创建haproxy配置文件

这里其他的配置都不用动,就后面server改成你电脑的配置

# haproxy.cfg
global
#工作目录
chroot /usr/local/etc/haproxy
#日志文件,使用rsyslog服务中local5日志设备(/var/log/local5),等级info
log 127.0.0.1 local5 info
#守护进程运行
daemon 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:8888
#访问协议
mode http
#URI相对地址
stats uri /dbs
#统计报告格式
stats realm Global\ statistics
#登陆帐户信息
stats auth admin:abc123456
#数据库负载均衡
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 172.18.0.2:3306 check weight 1 maxconn 2000
server MySQL_2 172.18.0.3:3306 check weight 1 maxconn 2000
server MySQL_3 172.18.0.4:3306 check weight 1 maxconn 2000
server MySQL_4 172.18.0.5:3306 check weight 1 maxconn 2000
server MySQL_5 172.18.0.6:3306 check weight 1 maxconn 2000
#使用keepalive检测死链
option tcpka

(三)、创建haproxy容器

命令:docker run -it -d -p 4001:8888 -p 4002:3306 -v /root/haproxy:/usr/local/etc/haproxy --name haproxy1 --privileged --net=mysql_net haproxy

这里把监控界面的8888端口映射到宿主机的4001,然后把数据库负载均衡的3306端口映射到宿主机的4002

演示:

 root@ataola  ~/haproxy  docker run -it -d -p 4001:8888 -p 4002:3306 -v /root/haproxy:/usr/local/etc/haproxy --name haproxy1 --privileged --net=mysql_net haproxy
0e4ef69c19b981163067ec8af0c38b6fa84c380855c22309a884f0a0ed691912
root@ataola  ~/haproxy 

之后我们进入到这个起起来的容器docker exec -it haproxy1 bash

执行配置命令:haproxy -f /usr/local/etc/haproxy/haproxy.cfg

(四)、创建haproxy数据库账号

打开MySQL数据库,创建一个用户CREATE USER 'haproxy'@'%' IDENTIFIED BY 'superman';

访问http://localhost:4001/dbs,就可以看到数据集群的情况。

MySQL集群搭建方案(PXC)-LMLPHP

看到这里就说明你的haproxy搭建成功了,接下来我们进行相关的实践。

相关实验

实验须知

这里我们在创建了一个test数据库,在数据库中创建一张数据表为user,并添加相应的字段和数据,具体的如下:

MySQL集群搭建方案(PXC)-LMLPHP

实验一:在主节点和从节点都完好的情况下,分别向主节点和从节点插入数据,看看其他节点的变化。

主节点插入:

我们尝试在主节点mysql_node1插入数据,然后去mysql_node_2去读取数据,这里我们就直接硬核的手动挡来吧(PS:初次加载时间长,如下图)

MySQL集群搭建方案(PXC)-LMLPHP

可以看到我们从mysql_node2节点读取到了mysql_node1主节点写入的内容。

从节点插入:

把楼上的例子反过来,这次我们从节点插入,主节点读取看看。

MySQL集群搭建方案(PXC)-LMLPHP

从这个实验,我们可以印证一点的是,主从节点都是可以读写的。

实验二:挂掉主节点,从节点看能不能插入

接下来我们就要开始搞事情了,把主节点停掉,再从节点插入数据看看。

命令:docker pause mysql_node1

演示:

MySQL集群搭建方案(PXC)-LMLPHP

这个时候主节点是打不开的,相当于宕机了。

MySQL集群搭建方案(PXC)-LMLPHP

MySQL集群搭建方案(PXC)-LMLPHP

我们尝试着在从节点插入数据,发现从节点也是打不开的

MySQL集群搭建方案(PXC)-LMLPHP

这印证了上面说的强一致性,同步机制为同步, 当主节点挂了以后,其余节点不可读写。

实验三:挂掉某个从节点,看看主从节点能否插入

我们把node3和node4服务给停掉,接着我们尝试着打开node1去插入一条张东升试试

MySQL集群搭建方案(PXC)-LMLPHP

MySQL集群搭建方案(PXC)-LMLPHP

可以看到在主节点插入数据,从节点也能够同步。

接着我们在node2从节点插入张朝阳

MySQL集群搭建方案(PXC)-LMLPHP

可以看到从节点插入成功,也同步到了主节点。

这个时候,我们把node3和node4起起来,看下数据会不会进行一个同步

MySQL集群搭建方案(PXC)-LMLPHP

可以看到,当node3和node4恢复的时候,便会进行一个数据同步,我们便在node3和node4中看到了张东升和张朝阳。

参考文献

percona介绍:https://hub.docker.com/_/percona

percona安装:https://www.percona.com/doc/percona-server/8.0/installation/docker.html

Docker环境下的前后端分离项目部署与运维: https://coding.imooc.com/class/219.html

最后

写到这里,笔者也只是记流水账一样,记录了当时的操作过程并加以复现,并没有对集群关于性能热备份冷备份等等方面进行深入探讨学习,这里仅作抛砖引玉,有兴趣的童鞋接力实践吧!

MySQL集群搭建方案(PXC)-LMLPHP
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

05-11 11:37