一、前言
我们在实际生产中,对于mysql数据库而言,基本上都是采用的高可用架构,没有谁还采用单个mysql数据库。因为单个mysql会存在单点故障、性能瓶颈等缺点,那么针对这个缺点,所以才会出现高可用的mysql数据库架构。既然提到高可用的架构,那就离不开架构的设计,怎么样的架构设计才算是合理,当然是跟你的业务所挂钩的。
二 、常用架构辨析
首先,我们需要知道高可用架构需要解决我们的哪些问题?
无外乎以下几点:
- 热备份, 多活, 故障切换
- 负载均衡、 读写分离
高可用架构一般分为以下几类:
(1)常规复制架构(Master --- Slaves)
在实际应用场景中, MySQL 复制 90% 以上都是一个 Master 复制到一个或者多个Slave 的架构模式
(2)主主复制架构(Master --- Master)
这种架构,如果在没有使用keepalived的情况下,就只针对mysql本身而言,写层只能有一个入口,其它的主充当从的角色,否则就会出现数据的不一致等问题。
(3)级联复制架构(Master --- Slaves --- Slaves ...)
(4)主主复制与级联复制结合架构(Master - Master - Slaves)
三、搭建
无论是采用上面讲到的哪种架构,都离开的关键词“复制”,因为多数据库实例之前想要实现数据的一致,就需要借助bin-log。
复制机制的实现原理如下:
下面主要以Master-Slave架构为例进行讲解。这里使用docker进行安装
1、准备两台虚拟机器
虚拟ip | 角色 |
192.168.1.105 | 主机 |
192.168.1.110 | 从机 |
2、下载镜像
可以使用以下命令,搜索有哪些版本,然后你可以选择你需要的版本
docker search mysql
我这里将使用mysql5.7的版本,输入以下命令即可下载镜像
docker pull mysql:5.7
下载完成后,可以使用以下命令进行查看
docker images
(3)开始搭建
待镜像下载完成后,就可以开始搭建工作了。
在两台虚拟机下都执行以下命令
docker run --name mysql3306 -p 3306:3306 --privileged=true -ti -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=mydb -e MYSQL_USER=user -e MYSQL_PASSWORD=pass -v /home/mysql/docker-data/3306/conf:/etc/mysql/conf.d -v /home/mysql/docker-data/3306/data/:/var/lib/mysql -v /home/mysql/docker-data/3306/logs/:/var/log/mysql -d mysql:5.7
注意,命令中“MYSQL_ROOT_PASSWORD”的值是给root用户的登录密码,“MYSQL_USER”是表示新建一个用户,“MYSQL_PASSWORD”是表示新建用户的登录密码
进入conf目录,创建文件my.cnf
主虚拟机的my.cnf内容如下:
[mysqld]
character_set_server=utf8
init_connect='SET NAMES utf8'
symbolic-links=0
server-id=1053306
log-bin=mysql-bin
auto_increment_increment=2 # 表示开启id为自增时,增量为2
auto_increment_offset=1 # 表示开启id为自增时,id从1开始
lower_case_table_names=1 # 表示忽略表名大小写
#binlog-do-db=mstest //要同步的mstest数据库,要同步多个数据库
#binlog-ignore-db=mysql //要忽略的数据库
从虚拟机的my.cnf内容如下:
[mysqld]
character_set_server=utf8
init_connect='SET NAMES utf8'
symbolic-links=0
server-id=1103306
log-bin=mysql-bin
auto_increment_increment=2 # 表示开启id为自增时,增量为2
auto_increment_offset=2 # 表示开启id为自增时,id从2开始
lower_case_table_names=1 # 表示忽略表名大小写
#binlog-do-db=mstest //要同步的mstest数据库,要同步多个数据库
#binlog-ignore-db=mysql //要忽略的数据库
注意:如果是在主主复制的情况,这个auto_increment_increment属性的值配置是有讲究的,比哪有三个主,则这个值为3,auto_increment_offset这个属性的值也是有讲究的,第一个主的里,这个值为1,第二为2,第三为3。这样做的目的,就是为了如果数据从不同的主上写入话,由于id是分开的,所以也不会冲突。
分别在主从虚拟机上执行以下命令
docker restart mysql3306
注意,这个mysql3306是容器名称,即 docker ps 命令展示出来的列表的names列。
如下图,可以使用账号密码登录一下
复制一个mysql脚本到主从虚拟机的/usr/sbin目录下
注意,我这里是因为我的机器还没有安装非docker形式的mysql,所以没mysql脚本文件,如果你的机器上有mysql的软件,则可以跳过此步。
在主虚拟机上,输入以下命令:
连接mysql
mysql -u root -p123456 -h 192.168.1.105 -P 3306
创建用户并授权
GRANT REPLICATION SLAVE,FILE,REPLICATION CLIENT ON *.* TO 'repluser'@'%' IDENTIFIED BY '123456';
刷新权限
FLUSH PRIVILEGES;
查看主机的状态
show master status;
在从虚拟机上输入以下命令:
连接mysql
mysql -u root -p123456 -h 192.168.1.110 -P 3306
指向master
change master to master_host='192.168.1.105',master_port=3306,master_user='repluser',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=621;
查看从机的状态
show slave status\G
从图中这两个线程都是停止状态,我们现在需要把它们开启
start slave;
分别在主从虚拟机上执行以下命令
SHOW PROCESSLIST;
到此,搭建过程完成,下面我们来看一下效果,是否真正地做到了主从复制呢?
在主库中那一张表如图
保存后,刷新两个库,可以看到从库中,也有了表了
接下来,我们添加一条数据,发现从库也能同步数据
如果我们在从库中添加一条数据呢,会不会同步到主库呢?刷新表
发现从库里面的添加的数据并没有同步到主库,那么再从主库里面添加一条数据呢
发现还是可以同步的。
到此,搭建主从数据库成功了。
我们现在需要思考一个问题,在一个项目开发的过程当中,是不是我们一开始就进行主从复制的架构呢?
显然,肯定不是,我们是结合数据的发展,发现架构不合理的时候,才进行架构的设计。所以在大部分情况下,当数据到达一定量的时候,才考虑从库的方案。那么这种情况下,我们又该怎么做,才能保证数据的一致性呢?
接下来就开始演示。
怎么使用docker启一个mysql数据,这里不再重复说明。现在我启动了一个数据库3310
mysql -u root -p123456 -h 192.168.1.110 -P 3310
还是重复上面的过程
change master to master_host='192.168.1.105',master_port=3306,master_user='repluser',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=2472;
start slave;
show slave status\G
show PROCESSLIST
接下来我们再去看一下数据会不会同步?
从图中,我们可以看到,这个表及数据并没有同步过来,这是为什么呢,那有没有解决方案呢?
对于这个问题,没有什么好的方案,只能手动的干预才行,
先要将主库中的数据手动同步到从库,正常情况下,需要使用热备份的方式做,在主库中,具体做法如下
1、 进入mysql查看数据库
mysql -uroot -p123456;
show databases;
2、 备份相应数据库
mysqldump -uroot -p123456 --databases consult mall > back.sql
这里需要注意, 如果是热备份, 这里需要在dump数据之前进行锁表操作, 避免dump数据的时候出
现插入操作导致数据不一致的情况。
3、 master锁表
锁表: flush table with read lock;
4、 slave导入数据
mysql -uroot -p123456 < back.sql
5、 master解锁
解锁: unlock tables;
但是这里我不这么做,我使用navicat导入导出脚本的方式,因为数据量很少。偷下懒。。。
我们再看下主库的状态
发现Position的值已经变化了,所以我们需要重新在从库中指定一下位置,否则还是无法同步数据
停止线程
stop slave;
指定位置
change master to master_host='192.168.1.105',master_port=3306,master_user='repluser',master_password='123456',master_log_file='mysql-bin.000001',master_log_pos=2738;
启动线程
start slave
查看进行状态
show slave status\G;
show PROCESSLIST;
然后我们再去添加一条数据
发现又可以同步数据了。
需要强调的时,我们在配置的过程当中,bin-log文件名和postion位置值一定要和主库中同步,可以在主库中使用“show master status”进行查看