前沿
现在的web开发中,数据库的主从备份、读写分离已经是一个必备的服务,无论从数据安全角度还是性能角度考虑,这都是必须增加的功能,如果开发使用的是阿里云或者腾讯云的数据库,基本上都有现成的服务可以使用,而本文要实现的是自建的基于docker来做的主从服务。
本文所有代码都已提交 github
原理
同步操作通过 3 个线程实现,其基本步骤如下:
- 主库在数据更新提交事务之前,将事件异步记录到binlog二进制日志文件中,日志记录完成后存储引擎提交本次事务
- 从库启动一个I/O线程与主库建立连接,用来请求主库中要更新的binlog。这时主库创建的binlog dump线程,这是二进制转储线程,如果有新更新的事件,就通知I/O线程;当该线程转储二进制日志完成,没有新的日志时,该线程进入sleep状态。
- 从库的I/O线程接收到新的事件日志后,保存到自己的relay log(中继日志)中
- 从库的SQL线程读取中继日志中的事件,并执行更新保存。
配置
主库
- server-id:服务器设置唯一ID,默认为1,推荐取IP最后部分;
- log-bin:设置二进制日志文件的基本名,默认不开启,配置后表示开启日志;
- log-bin-index:设置二进制日志索引文件名;
- binlog_format:控制二进制日志格式,进而控制了复制类型,三个可选值
- STATEMENT:SQL语句复制,优点:占用空间少,缺点:误删则无法恢复数据,在某些情况下,可能造成主备不一致
- ROW:行复制,优点:可以找回误删的信息,可以避免主备不一致的情况,缺点:占用空间大
- MIXED:混和复制,默认选项,混合statement,row。 Mysql 会判断哪些语句执行可能引起主备不一致,这些语句采用row 格式记录,其他的使用statement格式记录,当然这种形式的日志也没有办法恢复误删的数据。
- sync-binlog:默认为0,表示MySQL不控制binlog的刷新,由文件系统自己控制它的缓存的刷新。这时候的性能是最好的,但是风险也是最大的。一旦系统崩溃, binlog_cache中的所有binlog信息都会被丢失。为保证不会丢失数据,需设置为1,用于强制每次提交事务时,同步二进制日志到磁盘上。
- expire_logs_days:设置binlog保存时间,默认为0,也就是随着服务器运行,binlog会越来越大。看业务需求来配置binlog保存时间吧。结合每日的数据库备份功能,通过binlog,可以支持将数据库回溯到N天的任意时间点。
- max_binlog_size:binlog日志文件大小 默认大小1G
- binlog-do-db:binlog记录的数据库
- binlog-ignore-db:binlog 不记录的数据库
从库
- server-id:服务器设置唯一ID
- relay-log:中继日志
- relay-log-index:中继日志的索引文件
- read-only:是否只读,默认为0,为1表示只读
- replicate-do-db:同步的数据库
- replicate-ignore-db:不同步的数据库
- replicate-wild-do-table:同步的数据表
- replicate-wild-ignore-table:不同步的数据表
命令
从库配置主库账号密码
1
2
3
4
5mysql> CHANGE MASTER TO
MASTER_HOST='master',
MASTER_PORT=3306,
MASTER_USER='slave',
MASTER_PASSWORD='slave';
重置从库
1
mysql> RESET SLAVE;
启动从库
1
mysql> START SLAVE;
停止从库
1
mysql> STOP SLAVE;
查询主库状态
1
2
3
4
5
6
7
8mysql> show master status \G
*************************** 1. row ***************************
File: mysql-bin.000004
Position: 458
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set:
1 row in set (0.00 sec)查询从库状态
1
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60mysql> show slave status \G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: master
Master_User: slave
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000031
Read_Master_Log_Pos: 3146
Relay_Log_File: 1ab49ece5ea5-relay-bin.000033
Relay_Log_Pos: 3359
Relay_Master_Log_File: mysql-bin.000031
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table: data.%
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 3146
Relay_Log_Space: 5580
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: 0c77048c-e77d-11ea-9ba9-0242ac110002
Master_Info_File: /var/lib/mysql/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:
Executed_Gtid_Set:
Auto_Position: 0
Replicate_Rewrite_DB:
Channel_Name:
Master_TLS_Version:
1 row in set (0.00 sec)
上面标记的输出信息Slave_IO_Running: Yes和Slave_SQL_Running: Yes可以看到I/O线程和SQL线程已启动运行中。
核心代码说明
代码结构如下所示:
1 | . |
构建镜像
主从镜像都是基于 mysql 5.7.13 版本进行构建,区别在于:
- 主库镜像会在创建容器后生成一个用于同步数据的账号
slave
,密码和账号一致 - 从库镜像在创建容器后会配置下连接主库的账号密码并重置以及启动同步
主库镜像代码
- master.dockerfile
1 | # mysql 版本 |
- master.sh
1 | #!/bin/sh |
- master.sql
- REPLICATION CLIENT:连接主库的权限
- REPLICATION SLAVE:复制数据的权限
1 | # 授予slave账号连接和复制权限 |
从库镜像代码
- slave.dockerfile
1 | # mysql 版本 |
- slave.sh
1 | #!/bin/sh |
数据及配置
master、slave1、slave2 三个为主从数据库的数据及配置目录,为1主2从架构
- data 子目录为数据目录,启动容器后会将该目录下的数据映射到容器中
my.cnf 为数据库配置文件,这里也是配置主从的核心部分
1
2
3
4
5# 主库配置
log-bin=mysql-bin
server-id=1
# 同步黑名单
# binlog_ignore_db = information_schema,mysql,performance_schema,sys
1
2
3
4
5
6
7
8
9
10
11
# 从库配置
log-bin = mysql-bin
server-id = 2
# 当从库作为其他从库的主库时,该项需要配置为1,主要目的是将从主库复制的数据写入到bin-log中
log-slave-updates = 1
# 配置只读
read-only = 1
# 同步白名单
replicate_wild_do_table = data.%
# 同步黑名单
# replicate_wild_ignore_table = information_schema.%,mysql.%,performance_schema.%,sys.%
master/init.* 为主库初始化数据脚本
1
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# 创建数据库
create database if not exists data default charset=utf8mb4 collate=utf8mb4_general_ci;
# 切换数据库
use data;
# 用户表
create table if not exists user(
id int(11) unsigned auto_increment comment 'ID',
nickname varchar(15) not null comment '昵称',
gender tinyint(1) unsigned not null default 0 comment '性别 0:未设定 1:男 2:女',
phone char(11) not null comment '注册手机号',
status tinyint(1) not null default 1 comment '账号状态 0:未激活 1:启用 -1:禁用',
create_time int (10) unsigned not null comment '注册时间',
update_time int (10) unsigned not null comment '更新时间',
primary key (`id`),
unique index index_phone (`phone`),
index index_create_time (`create_time`)
)engine=innodb default charset=utf8mb4 collate=utf8mb4_general_ci comment '用户表';
# 添加测试数据
INSERT INTO user VALUES (1, 'test', 1, '18698277354', 1, 1598445824, 1598445824);
脚本(shell目录)
初始化(init.sh)
该脚本的作用是构建主库和从库镜像,并基于主库镜像启动一个容器,基于从库镜像启动两个从库容器,映射宿主机的端口分别为:3307、3308、3309
创建容器后会创建网络,将三个容器连接到一个网络中,之后容器内部连接使用的是容器别名
需要修改本脚本中的docker run参数的绝对路径地址
1 | #!/bin/bash |
测试(test.sh)
测试脚本会将主库的初始化脚本和sql复制到主库容器中,然后导入到数据库中
1 | #!/bin/bash |
销毁(destroy.sh)
销毁脚本会删掉网络、容器和所有镜像,但不会删除数据,可以自行手动删除,或者更改此脚本,增加删除data子目录部分
1 | #!/bin/sh |
使用
- 拷贝代码到本地指定目录
1 | git clone https://github.com/jormin/docker_mysql_master_slave ~/docker/mysql |
- 执行初始化脚本
1 | ➜ mysql git:(master) cd ~/docker/mysql |
执行完毕后可以看到已经创建好了主库和从库的镜像以及一个主容器和两个从容器,创建日志为 shell/init.log
1 | Sending build context to Docker daemon 6.144kB |
这个时候还没有执行测试脚本,三个容器中都没有data数据库,截图如下:
- 执行测试脚本
1 | mysql git:(master) ✗ bash shell/test.sh |
测试脚本会往主库中新建数据库data、数据表user以及一条数据,查看从库可以看到数据已经同步过来,至此主从同步已完成
- 执行销毁脚本
1 | ➜ mysql git:(master) ✗ bash shell/destroy.sh |
本文作者:Jormin
本文地址: https://blog.lerzen.com/基于docker实现mysql主从/
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 CN 许可协议。转载请注明出处!