CentOS7双主四从的MySQL集群
CentOS中搭建Docker环境
- 关闭SELINUX服务
和很多程序冲突,找到/etc/sysconfig/selunux文件,把SELTNUX设置为disabled,保存重启CentOS
1 | vi /etc/sysconfig/selinux |
2.docker安装
1 | yum update -y |
Linux的SSH服务软件
1.window MobaXterm
2.Mac Termius
双主四从的MySQL集群概念
一个高可用、高负载、高性能的“三高”MySQL集群,至少需要12个MySQL节点。
数据同步
单节点的MySQL抗风险性很差,就这么一个MySQL,如果遇上故障数据库挂了,项目没法用了,MySQL要有冗余节点。
既然要添加冗余节点,就必须借助于数据同步,保证两个MySQL节点数据完全一致。这样挂掉任何一个节点,另一个节点都能立即接替工作。MVSQL自带了Master-Slave数据同步模式,也被称作主从同步模式。例如MySQL A节点开启了binlog日志文件之后,MySQL A上面执行SQL语句(查询语句除外)都会被记录在binlog日志里面。MySQLB节点通过订阅MySQL A的binlog文件,能实时下载到这个日志文件,然后在MySQL B节点上运行这些SQL语句,于是就保证了自己的数据和MySQL A节点一致。
MySQL A被称作Master(主节点),MySQL B被称作Slave(从节点)。需要注意,主从同步模式里面,数据同步是单项的,如果你在MySQLA上写入数据,可以同步到MySQL B上面;如果在MySQL B上面写入数据,是不能同步到MySQL A节点的。假设MYSQL A挂了,MySQL B可以接替工作。但是如果MySQL A经过维修重新上线,MySQL A应该从MySQL B上同步数据,所以我们必须要给MySQL A和MySQL B设置双向主从同步,也就是互为主从节点。
读写分离
搭建MVSQL集群的时候,划定MVSQL1用来处理写任务,MySQL2和MySQL 3负责处理读请求。原来一个节点的处理所有SQL语句变成三个节点来分担,MSQL集群的读写性能比单节点MySQL是翻倍的,能并发执行的SQL语句条数也翻倍了。
如果MySQL 1宕机,MySQL 4可以接替MySQL1的工作。由于MySQL 2和MySQL 3是与MySQL 1同步,所以MySQL 1宕机之后,MySQL 2和MySQL 3的数据也就不能同步了。况且MySQL 2和MySQL 3不能自动切换到与MySQL 4同步。因此我们要给MySQL 4配置上两个读节点:MySQL 5和MySQL 6。也就是说MySQL 4接替MySQL1的时候,要用自己的两个从节点。相反MySQL 4宕机,MySQL1也是用自己的两个从节点。
数据切分
因为MySQL数据库单表数据量如果超过两千万,该表的读写性能会急剧下降,所以我们要做数据切分,俗称分库分表。比如说一个中型电商网站,每天能产生2万笔订单,算下来10年间大概会有7300万条订单记录。单节点MySQL单表保存这么多记录肯定吃不消,所以我们可以用10个MySQL节点共同承担数据存储任务。每个MySQL节点只存储730万条订单记录,压力并不大。
怎么能把7300万条记录切分存储到10个MVSQL节点上呢?其实有很多种算法,最简单的是按照主键求余数切分比如说每个订单记录的主键值对10求余数,余数范围是0~9之间。如果余数是0,这个INSERT语句被MyCat发送给MVSQL1执行;余数是1,INSERT语句被发送给MVSQL2执行,然后以此类推,由于每个MSQL节点可以被称作分片,所以我们可以说成数据被切分到10个分片之中。
如果每个分片都要用6个MySQL节点,10个分片总共就得用60个MySQL节点。对于一个中型电商网站来说,数据库集群的成本还能接受。也许有的同学担心日积月累,20年之后10个分片肯定也不够用,到时候应该怎么做呢?这个也不复杂,有两个办法。其一是做扩容,比如说再增加10个分片。增加分片就牵扯到数据重新切分,数据迁移的时间成本还是很高的。不到万不得已,一般不添加分片。其二是定期缩表,就是把几年前的数据迁移到MongoDB或者HBase这样的大数据库平台上面,它们保存PB级数据都毫无压力。我们只在MySQL集群中保存最近今年的数据,这样就能缓解数据存储的压力了。
为什么使用MySQL5.7版本?
因为MVSQL8.0的主从同步功能做的不好,MSQL宕机重新上线之后,数据不能自动同步,需要人工对比日志和维护,才能重新实现数据同步。比如MySQL1和MySQL2为双向主从同步,如果MySQL1宕机了两个小时,经过检查维修之后重新上线,按理说MySQL1应该自动去同步MySQL 2的数据。但是8.0版本的MySQL偏偏不行人工对比两个数据库之间日志的差异,然后执行若干的命令才能让MySQL 1成功同步到MySQL 2。如果MySQL 2一直不断的有新数据写入,人工核对日志的速度远远赶不上数据写入的速度,就只纰掉业务系统了。这么看来,一个宕机的MySQL节点想要重新上线,代价就是停掉前后端业务系统,这个成才
不应该盲目的追求MySQL新版本,稳定省心的MySQL5.7才是最佳方案,而且MySQL5.同样支持JSON字段类型,我们可以放心的使用。
为什么使用MyCat管理数据库集群
管理MySQL集群可以选用很多种中间件方案,比如说 myÇat、shardingsphere、proxysQL,MyCat是很老的中间件产品了,而且版本更新也不是很快,还不如选用 shardingsphere 呢。shardingSphere做数据切分的。如果不需要数据切分,只需要主从同步和读写分离就够了。shardingsphere 恰恰对这两块支持的特别不好。
搭建双主四从的MySQL集群
先创建一个自定义网络(子网段要和下面的 IP 对应):
1 | docker network create --subnet=172.18.0.0/16 mynet |
创建容器 mysql_1 主节点
1 | docker run -it -d --name mysql_1 \ |
- docker run :创建并运行一个新容器
- -it :交互式 + 伪终端(通常用来保持终端连接,搭配 -d 一起用其实没啥作用)
- -d :后台运行容器(detached 模式)
- –name mysql_1 :容器的名字叫 mysql_1
- -p 7001:3306 把宿主机的 7001 端口 映射到容器的 3306 端口(MySQL 默认端口)
- –net mynet :让容器加入一个用户自定义网络 mynet
- -m 400m 限制这个容器最多使用 400MB 内存,避免单个容器占用太多宿主机资源。
- -v 宿主机目录:容器目录 :做数据卷映射。
- /root/mysql_1/data:/var/lib/mysql :把 MySQL 的数据文件存放在宿主机。/root/mysql_1/data,实现数据持久化,容器删掉数据不会丢。
- /root/mysql_1/config:/etc/mysql/conf.d :把宿主机的配置目录挂载到容器,让你在宿主机写配置文件(如 my.cnf)直接生效。
- MYSQL_ROOT_PASSWORD=abc123456 :初始化 root 用户的密码。
- TZ=Asia/Shanghai :设置容器时区为上海(中国标准时间)。
- –privileged=true 让容器获得宿主机的 特权模式,几乎等同于 root 权限。
- mysql:5.7 :使用官方 MySQL 5.7 镜像。
- –lower_case_table_names=1 :强制 MySQL 表名不区分大小写(在 Linux 默认是区分大小写的)。
在Navicat上面给MySQL_1创建一个新账户,将来给MySQL_2和MySQL_3的从节点订阅binglog日志的使用用这个账户登录MySQL_1节点
1 | 用户名 sync |
my.cnf文件配置
1 | [mysqld] |
创建容器 mysql_2 从节点(mysql_1的从节点)
1 | docker run -it -d --name mysql_2 \ |
my.cnf文件配置
log_bin=mysql-bin 将来挂在更多读节点而准备的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 [mysqld]
#数据库字符集
character-set-server=utf8
#唯一ID,集群里不能重复
server-id=2
#开启binlog日志,规定日志文件名称
log_bin=mysql-bin
#开启relaylog日志,规定日志文件名称
relay_log=relay-bin
#限制普通用户无法INSERT、UPDATE、DELETE等操作,但对改配置对管理员无效
read_only=1
#采用严格的SQL模式
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
Navicat上面通过mysql_2节点执行SQL
1 | #停止数据同步服务 |
如果SQL语句执行结果中出现两个YES,说明主从同步就配置成功了。如果不成功,你就再检查上述的步骤,然后重新运行这几行SQL语句。或者先停掉容器,再删除容器和映射到 /root 目录的mysql目录,重新创建容器,严格遵守每个配置步骤,重新弄一遍主从同步。
创建容器 mysql_3 从节点(mysql_1的从节点)
1 | docker run -it -d --name mysql_3 \ |
my.cnf文件配置
log_bin=mysql-bin 将来挂在更多读节点而准备的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 [mysqld]
#数据库字符集
character-set-server=utf8
#唯一ID,集群里不能重复
server-id=3
#开启binlog日志,规定日志文件名称
log_bin=mysql-bin
#开启relaylog日志,规定日志文件名称
relay_log=relay-bin
#限制普通用户无法INSERT、UPDATE、DELETE等操作,但对改配置对管理员无效
read_only=1
#采用严格的SQL模式
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
创建容器 mysql_4 主节点
1 | docker run -it -d --name mysql_4 \ |
my.cnf文件配置
1 | [mysqld] |
创建容器 mysql_5 从节点(mysql_4的从节点)
1 | docker run -it -d --name mysql_5 \ |
my.cnf文件配置
log_bin=mysql-bin 将来挂在更多读节点而准备的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 [mysqld]
#数据库字符集
character-set-server=utf8
#唯一ID,集群里不能重复
server-id=5
#开启binlog日志,规定日志文件名称
log_bin=mysql-bin
#开启relaylog日志,规定日志文件名称
relay_log=relay-bin
#限制普通用户无法INSERT、UPDATE、DELETE等操作,但对改配置对管理员无效
read_only=1
#采用严格的SQL模式
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
Navicat上面通过mysql_5节点执行SQL
1 | #停止数据同步服务 |
创建容器 mysql_6 从节点(mysql_4的从节点)
1 | docker run -it -d --name mysql_6 \ |
my.cnf文件配置
log_bin=mysql-bin 将来挂在更多读节点而准备的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14 [mysqld]
#数据库字符集
character-set-server=utf8
#唯一ID,集群里不能重复
server-id=6
#开启binlog日志,规定日志文件名称
log_bin=mysql-bin
#开启relaylog日志,规定日志文件名称
relay_log=relay-bin
#限制普通用户无法INSERT、UPDATE、DELETE等操作,但对改配置对管理员无效
read_only=1
#采用严格的SQL模式
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
配置双向主从同步
1.配置MySQL_1节点,到MySQL_1执行4条SQL语句。以MySQL_4为主节点,订阅日志同步数据。
1 | #停止数据同步服务 |
2.配置MySQL_4节点
1 | #停止数据同步服务 |
MyCat管理数据库集群
要用于实现 MySQL 的读写分离、分库分表、多数据源整合等功能。
这是一个基于 MyCat 1.6.7.4 版本构建的镜像,适用于 Linux/amd64 架构
1 | docker pull liuyi71sinacom/mycat:1.6.7.4 |
docker 启动 MyCat 容器
1 | docker run -d \ |
确认宿主机配置目录
1.查看
1 | ls -l /root/mycat/conf |
2.获取默认 wrapper.conf
1 | docker run --name mycat_temp -it liuyi71sinacom/mycat:1.6.7.4 /bin/bash |
这样 /root/mycat/conf 就有完整的默认配置
3.自定义 server.xml 和 schema.xml
server.xml
1 | <?xml version="1.0" encoding="UTF-8"?> |
schema.xml1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42<?xml version="1.0"?>
<!DOCTYPE mycat:schema SYSTEM "schema.dtd">
<mycat:schema xmlns:mycat="http://io.mycat/">
<schema name="his" checkSQLschema="true" sqlMaxLimit="100" randomDataNode="dn1">
<table name="tb_action" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_customer" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_dept" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_goods" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_module" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_permission" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_role" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_user" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_rule" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_order" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_appointment" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_appointment_restriction" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_system" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_checkup_report" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_customer_location" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_flow_regulation" primaryKey="id" dataNode="dn1" type="global" />
<table name="tb_customer_im" primaryKey="id" dataNode="dn1" type="global" />
</schema>
<dataNode name="dn1" dataHost="dh1" database="his"/>
<dataHost name="dh1" maxCon="1000" minCon="10" balance="1" writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100">
<heartbeat>select 1</heartbeat>
<!-- 主节点1 -->
<writeHost host="w1" url="172.18.0.2:3306" user="root" password="abc123456">
<readHost host="w1r1" url="172.18.0.3:3306" user="root" password="abc123456"/>
<readHost host="w1r2" url="172.18.0.4:3306" user="root" password="abc123456"/>
</writeHost>
<!-- 主节点2 -->
<writeHost host="w2" url="172.18.0.5:3306" user="root" password="abc123456">
<readHost host="w2r1" url="172.18.0.6:3306" user="root" password="abc123456"/>
<readHost host="w2r2" url="172.18.0.7:3306" user="root" password="abc123456"/>
</writeHost>
</dataHost>
</mycat:schema>
Redis
1 | docker pull redis:6.0.10 |
/root/redis/conf/redis.conf
1 | bind 0.0.0.0 |
MongoDB
1 | docker pull mongo:4.4.7 |
/root/mongo/mongod.conf
1 | net: |
RabbitMQ
1 | docker pull rabbitmq:3.8.9-management |
访问 Web 管理界面:
通过浏览器访问 http://<宿主机的IP>:15672,例如 http://localhost:15672。
默认的登录凭据是:
用户名:admin
密码:abc123456
minio
一款高性能的对象存储解决方案,在文件存储场景中被广泛采用,主要源于其独特的技术特性和优势,尤其适合现代云原生架构和大规模数据存储需求。
1 | docker pull minio/minio |
创建/root/minio/data文件夹,然后设置文件夹权限,不然无法存储文件。
1 | chmod -R 777 /root/minio/data |
1 | docker run -d \ |
http://localhost:9001/ 访问管理页面