51工具盒子

依楼听风雨
笑看云卷云舒,淡观潮起潮落

实战在 Docker 中部署 MySQL 8.0 主从模式

实战在 Docker 中部署 MySQL 8.0 主从模式 第1张


为什么需要 Mysql 主从复制

谈起为什么在大多数情况下部署 Mysql 常常使用 Mysql 主从模式进行部署这个问题,本来也去网上搜寻了一些答案,其中原因主要有以下几点:

  • 做数据的热备,主库宕机后备库能够及时替换主库,保证业务可用性,能一定程度避免数据丢失。
  • 实现读写分离,主库写,从库读,减小主库的读写压力。当主库执行写过程加锁时,不会堵塞从库读操作,从而提高了数据的查询效率。
  • 应对业务量越来越大,I/O 访问频率过高,单机无法满足的问题。增加多个从库做负载,能够降低整体 I/O 访问频率,提高单个机器 I/O 性能。

数据库常用的主从复制方式

  • 基于 Binlog 复制模式

MySQL 主从复制默认是异步的模式。MySQL增删改操作会全部记录在 Binlog 中,当 slave 节点连接 master 时,会主动从 master 处获取最新的 Binlog 文件。并把 Binlog 存储到本地的 relay log 中,然后去执行 relay log 的更新内容。

  • GTID 复制模式

在传统的复制里面,当发生故障,需要主从切换,需要找到 Binlog 和 位点信息,恢复完成数据之后将主节点指向新的主节点。在 MySQL 5.6 里面,提供了新的数据恢复思路,只需要知道主节点的 IP、端口以及账号密码就行,因为复制是自动的,MySQL 会通过内部机制 GTID 自动找点同步。接下来我们要部署的主从也是基于这种模式。

GTID 概念相关介绍

GTID 是什么

GTID 指的是全局事务 ID,全程是 Global Transaction Identifier,在整个事务流程中每一个事务 ID 是全局唯一的,且在整个主从复制架构中该 ID 都不会相同。

GTID 主从复制方式

基于 GTID 的主从复制方式的出现,主要是用于替换传统的 日志点 复制方式。通过 GTID 可以保证每个主库提交的事务在集群中都有 唯一 的一个事务 ID。强化了数据库主从的一致性和故障恢复数据的容错能力,在 主库 宕机发生 主从切换 的情况下,GTID 方式可以让其他从库自动找到新主库复制的位置。而且 GTID 可以忽略已经执行过的事务,减少了数据发生错误的概率。

GTID 的组成

GTID 由 server_uuid + tid 组成,其中:

  • server_uuid: server_uuid 是在 Mysql 首次启动过程中自动生成的一个 uuid(128位) 随机值,生成后会将该值存储到数据目录的 auto.cnf 中。因为是随机值,所以不同服务器的 Mysql 的 server_uuid 都是不相同的。
  • tid: 代表了该实例上已经提交的事务数量,是一个整数,初始值是 1,每次提交事务的时候分配给这个事务并加 1

其组成样式如下:

fb90fba5-60cf-11eb-b5fa-000c295fbc5f:21

GTID 复制工作原理

假设从库开启了 binlog,那么执行流程如下:

  • ① 主节点执行事务提交前会产生一个 GTID,其会随着事务一起记录到 binlog 日志中。
  • ② 从节点 I/O Thread 会读取主节点的 binlog 日志文件并存储在从节点的 relaylog 日志中。从节点将主节点的 GTID 这个值配置到 gtid_next 中,即下一个要读取的 GTID 值。
  • ③ 从节点读取 gtid_next 中的值,然后查找自己的 binlog 日志中是否有这个 GTID
  • ④ 如果有这个记录,说明这个 GTID 的事务已经执行过了,就忽略掉。
  • ⑤ 如果没有这个记录,从节点就会执行该 GTID 事务,并记录到自己的 binlog 日志中。在读取执行事务前会先检查其他 session 中是否持有该 GTID,确保不被重复执行。
  • ⑥ 在解析过程中会判断是否有主键,如果没有就用二级索引,如果没有就用全部扫描。

GTID 使用中的限制条件

GTID 复制是针对事务来说的,一个事务只对应一个 GTID,好多的限制就在于此。其中主要限制如下:

  • 不能使用 create table table_name select * from table_name
  • 在一个事务中既包含事务表(使用 InnoDB 存储引擎的表)的操作又包含非事务表(使用 MyISAM 存储引擎的表)。
  • 不支持创建或删除临时表操作,如 CREATE TEMPORARY TABLE or DROP TEMPORARY TABLE 语句操作。
  • 使用 GTID 复制从库跳过错误时,不支持执行该 ql_slave_skip_counter 参数的语法。

如何开启 GTID 模式

需要在 Mysql 配置文件中添加下面几条配置:

## 开启 gtid 模式
gtid_mode=on

## 配置不允许任何事务违反 GTID 一致性,用于保证数据一致性
enforce_gtid_consistency=on

## 开启 Binlog 并指定名称(可选)
log_bin=binlog

## 从节点从主节点接收到更新且执行,是否将记录存到从节点的 binlog 日志中(可选)
log-slave-updates=1

## 当从数据库启动的时候,从节点不会启动复制(可选)
skip-slave-start=1

准备部署环境

| 服务器IP | 角色 | 配置文件目录 | 数据存储目录 | |--------------|-----|-------------|------------------| | 192.168.2.11 | 主节点 | /apps/mysql | /data/mysql/data | | 192.168.2.12 | 从节点 | /apps/mysql | /data/mysql/data |

系统环境

  • Mysql 版本:8.0.23
  • Docker 版本:19.03.13

Docker 部署主节点

创建外挂的数据存储目录

创建 Mysql 的 Docker 镜像的外挂数据存储目录:

$ mkdir -p /apps/mysql/data

这个目录是本人创建的数据存储目录,实际部署需要修改为需要部署应用的服务器的存储目录。

创建主节点配置文件

在目录 /apps/mysql 中创建 Mysql 主节点的配置文件 my.cnf,内容如下:

[mysqld]
port = 3306
max_connections = 2000
default-time_zone='+8:00'

sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION

#gtid:
server_id = 1                   #服务器id
gtid_mode = on                  #开启gtid模式
enforce_gtid_consistency = on   #强制gtid一致性,开启后对于特定create table不被支持

#binlog
log_bin = mysql-binlog
log_slave_updates = on    
binlog_format = row             #强烈建议,其他格式可能造成数据不一致

#relay log
skip_slave_start = 1

default_authentication_plugin = 'mysql_native_password'  #更改加密方式

MySQL8.0 之前 mysql 密码加密方式为 mysql_native_password,而 MySQL8.0 版本默认新添加的用户密码默认使用的 caching_sha2_password,因此进行使用主从复制时可能会遇到错误,需要将加密方式改成 mysql_native_password。

部署 Mysql 主节点

部署 Mysql 的主节点,执行的 Docker 命令如下:

$ docker run --name mysql-a -d \
--restart=always \
--network=host \
-v /apps/mysql/my.cnf:/etc/mysql/conf.d/my.cnf \
-v /apps/mysql/data:/var/lib/mysql \
-v /etc/localtime:/etc/localtime \
-e MYSQL_ROOT_PASSWORD=123456 \
mysql:8.0.23
  • 配置 restart=always,设置容器挂掉后总是重启。
  • 配置 network=host,设置容器使用宿主机的网络。
  • 挂载 /apps/mysql/my.cnf 来配置 Mysql 配置文件。
  • 挂载 /apps/mysql/data 来配置 Mysql 数据存储。
  • 挂载 /etc/localtime 来配置 Docker 运行容器的时区。
  • 配置 MYSQL_ROOT_PASSWORD 环境变量,设置 Mysql 数据库的 root 密码。

创建鉴权用户

进入 MySQL 的 Master 容器中,使用客户端工具连接 MySQL Server:

$ docker exec -it mysql-a mysql -uroot -p123456

输入下面命令创建鉴权用户并赋与权限:

## 创建用户 repl 密码为 123456
$ create user 'repl'@'%' identified by '123456';

## 赋予用户 repl 权限
$ grant replication slave,replication client on *.* to 'repl'@'%';

## 刷新权限
$ flush privileges;

Docker 部署从节点

创建存储数据的目录

$ mkdir -p /apps/mysql/data

创建从节点配置文件

在目录 /apps/mysql 中创建 Mysql 从节点的配置文件,内容如下:

[mysqld]
port = 3306
max_connections = 2000
default-time_zone = '+8:00'

sql_mode=STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION

#GTID:
server_id = 2                  #服务器id
gtid_mode = on                 #开启gtid模式
enforce_gtid_consistency = on  #强制gtid一致性,开启后对于特定create table不被支持

#binlog
log_bin = mysql-binlog
log_slave_updates = on   
binlog_format = row            #强烈建议,其他格式可能造成数据不一致

#relay log
skip_slave_start = 1
default_authentication_plugin = 'mysql_native_password'  #更改加密方式

read_only = on                   #设置只读

部署 Mysql 从节点

执行下面 Docker 命令,部署 Mysql 从节点:

$ docker run --name mysql-b -d \
--restart=always \
--network=host \
-e MYSQL_ROOT_PASSWORD=123456 \
-v /apps/mysql/my.cnf:/etc/mysql/conf.d/my.cnf \
-v /apps/mysql/data:/var/lib/mysql \
-v /etc/localtime:/etc/localtime \
mysql:8.0.23

从节点连接主节点

进入 Mysql Slave 镜像内部,使用客户端工具连接该 Msyql Server:

$ docker exec -it mysql-b mysql -uroot -p123456

配置从库连接主库:

$ CHANGE MASTER TO
      master_host='192.168.2.11',
      master_port=3306,
      master_user='repl',
      master_password='123456',
      master_auto_position=1;

注:master_host 需要改为你的主节点 IP 地址,用户名密码要和上面创建的用户密码一致。

启动从库:

$ start slave;

查看连接状态:

$ show slave status\G


*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 192.168.2.11
                  Master_User: repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-binlog.000003
          Read_Master_Log_Pos: 886
               Relay_Log_File: node-02-relay-bin.000003
                Relay_Log_Pos: 1107
        Relay_Master_Log_File: mysql-binlog.000003
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB: 
          Replicate_Ignore_DB: 
           Replicate_Do_Table: 
       Replicate_Ignore_Table: 
      Replicate_Wild_Do_Table: 
  Replicate_Wild_Ignore_Table: 
                   Last_Errno: 0
                   Last_Error: 
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 886
              Relay_Log_Space: 3129553
              Until_Condition: None
               Until_Log_File: 
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File: 
           Master_SSL_CA_Path: 
              Master_SSL_Cert: 
            Master_SSL_Cipher: 
               Master_SSL_Key: 
        Seconds_Behind_Master: 0
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error: 
               Last_SQL_Errno: 0
               Last_SQL_Error: 
  Replicate_Ignore_Server_Ids: 
             Master_Server_Id: 1
                  Master_UUID: f5c69d56-694c-11eb-800e-000c29e6fc4e
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           Master_Retry_Count: 86400
                  Master_Bind: 
      Last_IO_Error_Timestamp: 
     Last_SQL_Error_Timestamp: 
               Master_SSL_Crl: 
           Master_SSL_Crlpath: 
           Retrieved_Gtid_Set: f5c69d56-694c-11eb-800e-000c29e6fc4e:1-8
            Executed_Gtid_Set: 13f0092c-694d-11eb-aba6-000c29262359:1-5,
f5c69d56-694c-11eb-800e-000c29e6fc4e:1-8
                Auto_Position: 1
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version: 
       Master_public_key_path: 
        Get_master_public_key: 0
            Network_Namespace: 

进行测试

主库创建测试库、表并插入数据

在主节点服务器上执行下面命令,进入 mysql 客户端:

$ docker exec -it mysql-master mysql -uroot -p123456

然后执行下面命令创建测试库、表并插入测试数据:

##创建名为 test 的数据库 
$ CREATE DATABASE test;

##使用 test 数据库
$ USE test;

##创建 user 表
$ CREATE TABLE `user`  (
  `id` int(0) NOT NULL,
  `name` varchar(20) NULL DEFAULT NULL
);

##插入测试数据
$ INSERT INTO `user` VALUES (1, 'mydlq');

从库查看是否存在库与表和对应数据

在从节点服务器上执行下面命令,进入 mysql 客户端:

$ docker exec -it mysql-slave mysql -uroot -p123456

然后执行下面命令

##查看全部数据库
$ SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test               |
+--------------------+

##使用 test 数据库
$ USE test;

##查看全部表
$ SHOW TABLES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test               |
+--------------------+

## 查看 user 表中数据
$ SELECT * FROM `user`;
+----+-------+
| id | name  |
+----+-------+
|  1 | mydlq |
+----+-------+

如果从库中能正常执行上面命令且存在测试数据,则证明主从配置没有问题。

参考地址

  • Mysql 5.7 Gtid内部学习(一) 导读
  • MySQL的GTID复制比传统复制的优势
  • Replication with Global Transaction Identifiers

本文转载自:「 小豆丁个人博客 」,原文:http://t.cn/A6tMD6yD ,版权归原作者所有。




推荐阅读 点击标题可跳转

《Docker是什么?》

《Kubernetes是什么?》

《Kubernetes和Docker到底有啥关系?》

《教你如何快捷的查询选择网络仓库镜像tag》

《Docker镜像进阶:了解其背后的技术原理》

《教你如何修改运行中的容器端口映射》

《k8s学习笔记:介绍&上手》

《k8s学习笔记:缩扩容&更新》

《Docker 基础用法和命令帮助》

《在K8S上搭建Redis集群》

《灰度部署、滚动部署、蓝绿部署》

《Docker垃圾清理》

《Kubernetes(k8s)底层网络原理刨析》

《容器环境下Node.js的内存管理》

《MySQL 快速创建千万级测试数据》

《Linux 与 Unix 到底有什么不同?》

《浅谈几种常见 RAID 的异同》

《老司机必须懂的MySQL规范》

《Docker中Image、Container与Volume的迁移》

《漫画|如何用Kubernetes搞定CICD》

《写给前端的Docker实战教程》

《16个概念带你入门 Kubernetes》

《程序员因接外包坐牢456天,长文叙述心酸真实经历》

《IT 行业老鸟,有话对你说》

《HTTPS 为什么是安全的?说一下他的底层实现原理?



免责声明:本文内容来源于网络,所载内容仅供参考。转载仅为学习和交流之目的,如无意中侵犯您的合法权益,请及时联系Docker中文社区!




赞(3)
未经允许不得转载:工具盒子 » 实战在 Docker 中部署 MySQL 8.0 主从模式