在 MySQL 配置主从复制
GTID 相比传统 binlog(基于 file + position)的复制方式,核心优势在于把复制定位从“物理位置”升级为“逻辑事务 ID”。在 binlog 模式下,从库需要依赖 binlog 文件名和执行位置来继续复制,一旦发生主从切换或故障恢复,就必须人工找到正确的日志位置,操作复杂且容易出错,还可能因为定位不准导致数据重复或遗漏。而 GTID 模式下,每个事务都有全局唯一的 ID,从库只需要记录自己已经执行过的 GTID 集合,主库发送事务时,从库会自动判断是否执行过该事务,未执行则执行,已执行则跳过,因此不再依赖 position,实现自动对齐复制进度。这使得主从切换更加简单、安全,恢复更自动化,同时也减少了人为操作带来的数据不一致风险,并且在复杂拓扑(如多源复制)下更容易管理和避免重复执行问题。
通过 Docker Compose 部署
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
| services:
mysql-master:
image: mysql:8.0
container_name: mysql-master
environment:
MYSQL_DATABASE: db
MYSQL_USER: user
MYSQL_PASSWORD: pass
MYSQL_ROOT_PASSWORD: root
command: >
--server-id=1
--log-bin=mysql-bin
--binlog-format=ROW
--gtid-mode=ON
--enforce-gtid-consistency=ON
--log-slave-updates=ON
ports:
- "3306:3306"
volumes:
- mysql_master_data:/var/lib/mysql
mysql-slave:
image: mysql:8.0
container_name: mysql-slave
restart: always
ports:
- "3307:3306"
environment:
MYSQL_ROOT_PASSWORD: root
command: >
--server-id=2
--relay-log=mysql-relay-bin
--gtid-mode=ON
--enforce-gtid-consistency=ON
--log-slave-updates=ON
--read-only=ON
volumes:
- mysql_slave_data:/var/lib/mysql
volumes:
mysql_master_data:
mysql_slave_data:
|
将以上 Docker Compose 文件保存至某个目录,例如 C:\Users\Admin\Desktop\docker 然后在当前目录下运行 docker compose up -d 命令启动,控制台输出如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
| C:\Users\Admin\Desktop\docker>docker compose up -d
[+] up 3/3
✔ Network docker_default Created 0.0s
✔ Volume docker_slave_data Created 0.1s
[+] up 5/5docker_master_data Created 0.1s
✔ Network docker_default Created 0.0s
✔ Volume docker_slave_data Created 0.1s
✔ Volume docker_master_data Created 0.1s
✔ Container mysql-slave Started 0.4s
✔ Container mysql-master Started 0.4s
C:\Users\Admin\Desktop\docker>
|
通过 docker ps 命令查看容器是否正常运行:
1
2
3
4
5
6
| C:\Users\Admin\Desktop\docker>docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c31150c279b5 docker.io/library/mysql:8.0 --server-id=2 --r... 3 minutes ago Up 3 minutes 0.0.0.0:3307->3306/tcp, 33060/tcp mysql-slave
43921bba8afd docker.io/library/mysql:8.0 --server-id=1 --l... 3 minutes ago Up 3 minutes 0.0.0.0:3306->3306/tcp, 33060/tcp mysql-master
C:\Users\Admin\Desktop\docker>
|
控制台输出表示 MySQL 主从全部正常运行,接着通过命令进入容器 docker exec -it mysql-master mysql -uroot -proot ,进入后创建从机用户,
1
2
3
| CREATE USER 'repl'@'%' IDENTIFIED BY 'repl';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%';
FLUSH PRIVILEGES;
|
同样通过命令 docker exec -it mysql-slave mysql -uroot -proot 进入从机,进行 GTID 主从配置,
1
2
3
4
5
6
| CHANGE REPLICATION SOURCE TO
SOURCE_HOST='mysql-master',
SOURCE_USER='repl',
SOURCE_PASSWORD='repl',
SOURCE_AUTO_POSITION=1,
GET_SOURCE_PUBLIC_KEY=1; #MySQL 8.0 默认使用 caching_sha2_password 认证,不加 GET_SOURCE_PUBLIC_KEY=1 会报错
|
接着启动复制,
通过以下命令查看是否启动成功:
输出如下:
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
60
61
62
63
64
65
66
67
| mysql> SHOW REPLICA STATUS\G;
*************************** 1. row ***************************
Replica_IO_State: Waiting for source to send event
Source_Host: mysql-master
Source_User: repl
Source_Port: 3306
Connect_Retry: 60
Source_Log_File: mysql-bin.000003
Read_Source_Log_Pos: 899
Relay_Log_File: mysql-relay-bin.000003
Relay_Log_Pos: 1115
Relay_Source_Log_File: mysql-bin.000003
Replica_IO_Running: Yes
Replica_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_Source_Log_Pos: 899
Relay_Log_Space: 3000043
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Source_SSL_Allowed: No
Source_SSL_CA_File:
Source_SSL_CA_Path:
Source_SSL_Cert:
Source_SSL_Cipher:
Source_SSL_Key:
Seconds_Behind_Source: 0
Source_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:
Replicate_Ignore_Server_Ids:
Source_Server_Id: 1
Source_UUID: d1102c57-3a3c-11f1-aac5-6ad0b3b2c0b9
Source_Info_File: mysql.slave_master_info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Replica_SQL_Running_State: Replica has read all relay log; waiting for more updates
Source_Retry_Count: 86400
Source_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Source_SSL_Crl:
Source_SSL_Crlpath:
Retrieved_Gtid_Set: d1102c57-3a3c-11f1-aac5-6ad0b3b2c0b9:1-11
Executed_Gtid_Set: d0e5e05b-3a3c-11f1-a0a5-7622b0cdb3fc:1-5,
d1102c57-3a3c-11f1-aac5-6ad0b3b2c0b9:1-11
Auto_Position: 1
Replicate_Rewrite_DB:
Channel_Name:
Source_TLS_Version:
Source_public_key_path:
Get_Source_public_key: 1
Network_Namespace:
1 row in set (0.00 sec)
ERROR:
No query specified
|
出现以上提示 Replica_IO_Running: Yes Replica_SQL_Running: Yes 代表主从复制已经建立成功。
验证主从复制
第一步,在主库创建测试数据
docker exec -it mysql-master mysql -uroot -proot
1
2
3
4
5
6
7
8
9
10
11
12
13
| -- 创建测试数据库
CREATE DATABASE test_db;
USE test_db;
-- 创建测试表
CREATE TABLE users (
id INT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入数据
INSERT INTO users (name) VALUES ('张三'), ('李四'), ('王五');
|
第二步,在从库查询,确认数据已同步
docker exec -it mysql-slave mysql -uroot -proot
1
2
| USE test_db;
SELECT * FROM users;
|
预期输出:
1
2
3
4
5
6
7
8
| +----+------+---------------------+
| id | name | created_at |
+----+------+---------------------+
| 1 | | 2026-04-17 07:11:29 |
| 2 | | 2026-04-17 07:11:29 |
| 3 | | 2026-04-17 07:11:29 |
+----+------+---------------------+
3 rows in set (0.00 sec)
|
第三步,验证从库只读保护
先在从库开启 super-read-only
docker exec -it mysql-slave mysql -uroot -proot
1
| SET GLOBAL super_read_only = ON;
|
使用非 root 用户登录进行测试,由于主从复制会将 user 也复制过来,所以直接用主库配置的 user 登录即可
docker exec -it mysql-slave mysql -uuser -ppass
1
| INSERT INTO users (name) VALUES ('测试');
|
预期报错:
1
| ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement
|
问题排查
- 1. caching_sha2_password 认证失败 MySQL 8.0 默认使用
caching_sha2_password 插件,从库连接主库时需要在 CHANGE REPLICATION SOURCE TO 中加上 GET_SOURCE_PUBLIC_KEY=1,否则会报认证错误导致复制无法建立。 - root 用户可以绕过 read_only
--read-only=ON 对拥有 SUPER 权限的用户(如 root)无效,生产环境必须使用 super_read_only,单独的 read_only 无法保护从库。 - Windows 终端中文乱码 Windows CMD 默认使用 GBK 编码,连接 MySQL 时需要加上
--default-character-set=utf8mb4 参数,或改用 DBeaver、Navicat 等 GUI 工具操作。