Bootstrap

【存储中间件】MyCat2应用与实战(三):读写分离之Docker搭建MySQL主从复制集群

在这里插入图片描述
个人主页:道友老李
欢迎加入社区:道友老李的学习社区

5.Mycat2实现读写分离

读写分离原理:读写分离就是让主库处理事务性操作,从库处理select查询。数据库复制被用来把事务性查询导致的数据变更同步到从库,同时主库也可以select查询。

实现读写分离是基于MySQL的主从复制架构的,通过一主多从的配置方式,可以将查询请求均匀的分散到多个数据副本,能够进一步的提升系统的处理能力。 所以这里我们先要搭建一下MySQL的主从复制集群。

image.png

5.1 Docker搭建MySQL主从复制集群

5.1.1 MySQL主从同步原理

MySQL通过binlog完成主备同步,实现最终一致性。

  • Mysql 中有一种日志叫做 binlog(二进制日志)。这个日志会记录下所有修改了数据库的SQL 语句(insert,update,delete,create/alter/drop table, grant 等等)
  • 主从复制的原理其实就是把主服务器上的 binlog 复制到从服务器上执行一遍,这样从服务器上的数据就和主服务器上的数据相同了。

与主从复制相关的线程有三个

  • 主库上的dump_thread
  • 备库上的io_thread、sql_thread

与主从复制相关的日志有binlog、relaylog

  • binlog: 记录数据库的写入操作,以二进制的形式保存在日志文件;
  • relaylog: Slave 接收 Master 的同步日志,会先放到中继日志 relay log 里, slave 的读操作也会基于 relay log。

image.png

  1. master将数据改变记录到 二进制日志(binary log)中。
  2. 当slave上执行 start slave 命令之后,slave会创建一个 IO 线程用来连接master,请求master中的binlog。
  3. 当slave连接master时,master会创建一个 **binlog dump 线程,用于发送 binlog 的内容。在读取 binlog 的内容的操作中,会对主节点上的 binlog 加锁,当读取完成并发送给从服务器后解锁。
  4. IO 线程接收主节点 binlog dump 进程发来的更新之后,保存到 中继日志(relay log) 中。
  5. slave的 SQL线程,读取relay log日志,并解析成具体操作,从而实现主从操作一致,最终数据一致。

主从部署必要条件

  • 主库开启binlog日志(设置log-bin参数)
  • 主从server-id不同
  • 从库服务器能连通主库

服务器创建方式

  • 使用 docker方式创建MySQL服务器,主从服务器IP一致,使用端口号区分。

image.png

5.1.2 主服务器搭建

  1. 在docker中创建并启动MySQL主服务器:端口3306
docker run -d \
-p 3306:3306 \
-v /msb/mysql/master/conf:/etc/mysql/conf.d \
-v /msb/mysql/master/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name msb-mysql-master \
mysql:8.0.29
  1. 创建MySQL主服务器配置文件

默认情况下MySQL的binlog日志是自动开启的,可以通过如下配置定义一些可选配置

vim /msb/mysql/master/conf/my.cnf

[mysqld]
# 服务器唯一id,默认值1
server-id=1
# 设置日志格式,默认值ROW
binlog_format=STATEMENT
# 二进制日志名,默认binlog
# log-bin=binlog
# 设置需要复制的数据库,默认复制全部数据库
#binlog-do-db=mytestdb
# 设置不需要复制的数据库
#binlog-ignore-db=mysql
#binlog-ignore-db=infomation_schema

重启MySQL容器

docker ps

docker restart msb-mysql-master

binlog格式说明:

  • binlog_format=STATEMENT:日志记录的是主机数据库的 写指令,性能高,但是now()之类的函数以及获取系统参数的操作会出现主从数据不同步的问题。
  • binlog_format=ROW(默认):日志记录的是主机数据库的 写后的数据,批量操作时性能较差,解决now()或者 user()或者 @@hostname 等操作在主从机器上不一致的问题。
  • binlog_format=MIXED:是以上两种level的混合使用,有函数用ROW,没函数用STATEMENT,但是无法识别系统变量
  1. 登录MySQL主服务器

    ALTER USER ‘root’@‘%’ IDENTIFIED WITH mysql_native_password BY ‘123456’;

#启动容器,在容器内访问MySQL env LANG=C.UTF-8 避免容器中显示中文乱码
[root@localhost ~]# docker exec -it msb-mysql-master env LANG=C.UTF-8 /bin/bash

bash-4.4# mysql -uroot -p
Enter password: 输入密码

#修改默认密码校验方式
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
  1. 在主数据库上, 创建一个允许从数据库来访问的用户账号

    用户: msb_slave

    密码:123456

    主从复制使用 REPLICATION SLAVE 赋予权限

-- 创建slave用户
CREATE USER 'msb_slave'@'%';

-- 设置密码
ALTER USER 'msb_slave'@'%' IDENTIFIED WITH mysql_native_password BY '123456';

-- 授予复制权限
GRANT REPLICATION SLAVE ON *.* TO 'msb_slave'@'%';

-- 刷新权限
FLUSH PRIVILEGES;
  1. 主机中查询master状态

执行完此步骤后,不要再操作主服务器,防止主服务器状态值变化,可以选择锁住主服务器

-- 执行以下命令锁定数据库以防止写入数据。
FLUSH TABLES WITH READ LOCK;

到主服务器上查看主机状态, 记录File和Position对应的值

-- 在主机查看mater状态
SHOW MASTER STATUS;

+---------------+----------+--------------+------------------+-------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+----------+--------------+------------------+-------------------+
| binlog.000003 |     1092 |              |                  |                   |
+---------------+----------+--------------+------------------+-------------------+

+---------------+----------+--------------+------------------+-------------------+
| File          | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+---------------+----------+--------------+------------------+-------------------+
| binlog.000003 |     1345 |              |                  |                   |
+---------------+----------+--------------+------------------+-------------------+

5.1.3 从服务器搭建

我们可以选择配置多台从服务器,配置方式都是一样的。

  1. 在docker中创建并启动MySQL从服务器:名称 msb-mysql-slave1, 端口 3307
docker run -d \
-p 3307:3306 \
-v /msb/mysql/slave1/conf:/etc/mysql/conf.d \
-v /msb/mysql/slave1/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name msb-mysql-slave1 \
mysql:8.0.29

docker run -d \
-p 3308:3306 \
-v /msb/mysql/slave2/conf:/etc/mysql/conf.d \
-v /msb/mysql/slave2/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name msb-mysql-slave2 \
mysql:8.0.29
  1. 创建slave1的配置文件
vim /msb/mysql/slave1/conf/my.cnf

vim /msb/mysql/slave2/conf/my.cnf

配置下面的内容

[mysqld]
# 服务器唯一id,每台服务器的id必须不同,如果配置其他从机,注意修改id
server-id=2

[mysqld]
server-id=3

重启从服务器

docker restart msb-mysql-slave1

docker restart msb-mysql-slave2

登录MySQL主服务器

[root@localhost ~]# docker exec -it msb-mysql-slave1 env LANG=C.UTF-8 /bin/bash

bash-4.4# mysql -uroot -p
Enter password: 输入密码

ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';

[root@localhost ~]# docker exec -it msb-mysql-slave2 env LANG=C.UTF-8 /bin/bash

bash-4.4# mysql -uroot -p
Enter password: 输入密码

ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
  1. 在从机上配置主从关系
CHANGE MASTER TO MASTER_HOST='192.168.58.100', 
MASTER_USER='msb_slave',MASTER_PASSWORD='123456', MASTER_PORT=3306,
MASTER_LOG_FILE='binlog.000003',MASTER_LOG_POS=1345; 
  1. 启动从服务器,查看状态
START SLAVE;
-- 查看状态(不需要分号)
SHOW SLAVE STATUS\G

5.1.4 主从同步测试

在主库创建数据库、表,插入数据,测试从库是否同步数据

-- 创建数据库
CREATE DATABASE user_db CHARACTER SET utf8;

-- 创建表
CREATE TABLE users (
  id INT(11) PRIMARY KEY AUTO_INCREMENT,
  NAME VARCHAR(20) DEFAULT NULL,
  age INT(11) DEFAULT NULL
); 

-- 插入数据
INSERT INTO users VALUES(NULL,'user1',20);
INSERT INTO users VALUES(NULL,'user2',21);
INSERT INTO users VALUES(NULL,'user3',22);

5.1.5 停止和重置

需要的时候,可以使用如下SQL语句

-- 在从机上执行。功能说明:停止I/O 线程和SQL线程的操作。
stop slave; 

-- 在从机上执行。功能说明:用于删除SLAVE数据库的relaylog日志文件,并重新启用新的relaylog文件。
reset slave;

-- 在主机上执行。功能说明:删除所有的binglog日志文件,并将日志索引文件清空,重新开始所有新的日志文件。
-- 用于第一次进行搭建主从库时,进行主库binlog初始化工作;
reset master;

5.1.6 常见问题解决

启动主从同步后,常见错误是 Slave_IO_Running: No 或者 Connecting 的情况

image.png

解决方案1:

  1. 首先停掉Slave服务
-- 在从机停止slave
stop slave;
  1. 到主服务器上查看主机状态, 记录File和Position对应的值
-- 在主机查看mater状态
SHOW MASTER STATUS;

image.png

  1. 然后到slave服务器上执行手动同步:
-- MASTER_LOG_FILE和MASTER_LOG_POS与主库保持一致
CHANGE MASTER TO MASTER_HOST='192.168.58.100', 
MASTER_USER='slave',
MASTER_PASSWORD='123456',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000015',
MASTER_LOG_POS=442,
MASTER_CONNECT_RETRY=10;

解决方案2

  1. 程序可能在slave上进行了写操作
  2. 也可能是slave机器重起后,事务回滚造成的.
  3. 一般是事务回滚造成的,解决办法
mysql> slave stop;
mysql> set GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
mysql> slave start;
;