MySQL 双主同步
1、什么是MySQL双主同步
MySQL双主同步,也称为双主互备(Master-Master Replication),是一种高可用性的数据库架构。在这种架构下,两台MySQL服务器互为主从,即Server A是Server B的主库,同时Server B也是Server A的主库。
简单来说,就是两个数据库节点之间建立了双向的数据复制通道,任何一方数据的变更都会同步到另一方。
🔄 核心工作原理
双向复制:
通常的主从复制是单向的(A → B)。
双主同步则是闭环的(A ⇋ B)。A产生的二进制日志(binlog)会被B拉取并执行,同时B产生的binlog也会被A拉取并执行。
读写能力:
理论上,两个节点都可以接受读写请求。但在实际生产环境中,为了防止数据冲突和保证一致性,通常会配置为"一主写,一主备"(即平时只往A写,B作为热备),或者通过中间件严格控制写入路由。
🛡️ 为什么需要双主同步?
高可用性:如果主库A宕机,系统可以迅速切换到主库B继续提供服务,避免单点故障导致业务中断。
数据冗余:数据在两个节点都有完整备份,提高了数据安全性。
负载均衡:在某些场景下,可以将读请求分摊到两个节点上,减轻单一数据库的压力。
2、使用环境
只需要一台虚拟机,改虚拟机必须安装好docker,使用docker创建4个容器进行mysql隔离来实现MySQL双主同步
| 角色 | 容器名 | 端口 | 版本 | 系统 |
|---|---|---|---|---|
| master | mysql-master1 | 3306 | MySQL 8.4.8 | redhat 9.x |
| master | mysql-master2 | 3308 | MySQL 8.4.8 | redhat 9.x |
| slave | mysql-slave1 | 3307 | MySQL 8.4.8 | redhat 9.x |
| slave | mysql-slave2 | 3309 | MySQL 8.4.8 | redhat 9.x |
3、安装docker
找到docker-ce

点击docker-ce后找到对应系统的docker安装方式

bash
# step 1: 安装必要的一些系统工具
sudo yum install -y yum-utils
# Step 2: 添加软件源信息
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 注意这里网址的centos改为自己对应系统的名字,这里我所使用的是rhel,网站就应该是https://mirrors.aliyun.com/docker-ce/linux/rhel/docker-ce.repo
# Step 3: 安装Docker
sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y
# Step 4: 配置国内镜像加速器
# 创建目录
sudo mkdir -p /etc/docker
# 写入配置文件
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://docker-0.unsee.tech",
"https://docker-cf.registry.cyou",
"https://docker.1panel.live"
]
}
EOF
# 重启docker服务
sudo systemctl daemon-reload && sudo systemctl restart docker
# 注意:
# 官方软件源默认启用了最新的软件,您可以通过编辑软件源的方式获取各个版本的软件包。例如官方并没有将测试版本的软件源置为可用,您可以通过以下方式开启。同理可以开启各种测试版本等。
# vim /etc/yum.repos.d/docker-ce.repo
# 将[docker-ce-test]下方的enabled=0修改为enabled=1
#
# 安装指定版本的Docker-CE:
# Step 1: 查找Docker-CE的版本:
# yum list docker-ce.x86_64 --showduplicates | sort -r
# Loading mirror speeds from cached hostfile
# Loaded plugins: branch, fastestmirror, langpacks
# docker-ce.x86_64 17.03.1.ce-1.el7.centos docker-ce-stable
# docker-ce.x86_64 17.03.1.ce-1.el7.centos @docker-ce-stable
# docker-ce.x86_64 17.03.0.ce-1.el7.centos docker-ce-stable
# Available Packages
# Step2: 安装指定版本的Docker-CE: (VERSION例如上面的17.03.0.ce.1-1.el7.centos)
# sudo yum -y install docker-ce-[VERSION]
使用docker拉取一个mysql:8.4.8的镜像
bash
docker pull mysql:8.4.8

1、创建网络
bash
[root@localhost ~]# docker network create mysql_net
bash
# 创建对应的目录文件
mkdir -p /data/{master1,master2,slave1,slave2}/{log,data,conf}
2、修改两台master的配置文件
sh
[root@localhost ~]# cd /data/master1/conf/
[root@localhost conf]# vim my.cnf
# mysql-master1
[mysqld]
server_id=11
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
log-bin=mysql-bin
auto-increment-increment=2
auto-increment-offset=1
[root@localhost ~]# cd /data/master2/conf/
[root@localhost conf]# vim my.cnf
# mysql-master2
[mysqld]
server_id=33
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
log-bin=mysql-bin
auto-increment-increment=2
auto-increment-offset=2
3、创建两个master容器
sh
# mysql-master1
docker run --name mysql-master1 \
--restart=always \
-p 3306:3306 \
-v /data/master1/log:/var/log/mysql \
-v /data/master1/data:/var/lib/mysql \
-v /data/master1/conf/my.cnf:/etc/my.cnf \
-e MYSQL_ROOT_PASSWORD=123456 \
--network mysql_net \
-d mysql:8.4.8
# mysql-master2
docker run --name mysql-master2 \
--restart=always \
-p 3308:3306 \
-v /data/master2/log:/var/log/mysql \
-v /data/master2/data:/var/lib/mysql \
-v /data/master2/conf/my.cnf:/etc/my.cnf \
-e MYSQL_ROOT_PASSWORD=123456 \
--network mysql_net \
-d mysql:8.4.8
4、配置两台slave
sh
[root@localhost conf]# cd /data/slave1/conf/
[root@localhost conf]# vim my.cnf
# mysql-slave1
[mysqld]
server_id=22
log-bin=mysql-slave-bin
relay_log=mysql-relay
read_only=1
[root@localhost conf]# cd /data/slave2/conf/
[root@localhost conf]# vim my.cnf
# mysql-slave2
[mysqld]
server_id=44
log-bin=mysql-slave-bin
relay_log=mysql-relay
read_only=1
5、创建两台slave
sh
# mysql-slave1
docker run --name mysql-slave1 \
--restart=always \
-p 3307:3306 \
-v /data/slave1/log:/var/log/mysql \
-v /data/slave1/data:/var/lib/mysql \
-v /data/slave1/conf/my.cnf:/etc/my.cnf \
-e MYSQL_ROOT_PASSWORD=123456 \
--network mysql_net \
-d mysql:8.4.8
# mysql-slave2
docker run --name mysql-slave2 \
--restart=always \
-p 3309:3306 \
-v /data/slave2/log:/var/log/mysql \
-v /data/slave2/data:/var/lib/mysql \
-v /data/slave2/conf/my.cnf:/etc/my.cnf \
-e MYSQL_ROOT_PASSWORD=123456 \
--network mysql_net \
-d mysql:8.4.8
bash
[root@localhost conf]# tree /data/
/data/
├── master1
│ ├── conf
│ │ └── my.cnf
│ ├── data
│ └── log
├── master2
│ ├── conf
│ │ └── my.cnf
│ ├── data
│ └── log
├── slave1
│ ├── conf
│ │ └── my.cnf
│ ├── data
│ └── log
└── slave2
├── conf
│ └── my.cnf
├── data
└── log
6、在master1上创建用户
sql
# mysql-master1
[root@localhost ~]# docker exec -it mysql-master1 /bin/bash
bash-5.1# mysql -uroot -p'123456'
mysql> create user 'repl_user'@'%' identified by '123456';
Query OK, 0 rows affected (0.01 sec)
mysql> create user 'slave_sync_user'@'%' identified by '123456';
Query OK, 0 rows affected (0.00 sec)
mysql> grant replication slave on *.* to 'repl_user'@'%';
Query OK, 0 rows affected (0.00 sec)
mysql> grant replication slave on *.* to 'slave_sync_user'@'%';
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql> show binary log status;
+------------------+----------+--------------+--------------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+--------------------------+-------------------+
| mysql-bin.000003 | 1448 | | mysql,information_schema | |
+------------------+----------+--------------+--------------------------+-------------------+
1 row in set (0.00 sec)
7、配置master1和slave1同步
sql
[root@localhost ~]# docker exec -it mysql-slave1 /bin/bash
bash-5.1# mysql -uroot -p'123456'
# mysql-slave1
mysql> change replication source to \
-> source_host='mysql-master1',
-> source_user='slave_sync_user',
-> source_password='123456',
-> source_log_file='mysql-bin.000003',
-> source_log_pos=1448, # 注意这里的位置一定是在master1中执行show binary log status;之后显示的那个位置id和file的,一定和自己显示的一样
-> get_source_public_key=1;
Query OK, 0 rows affected, 2 warnings (0.01 sec)
mysql> start replica;
Query OK, 0 rows affected (0.02 sec)
mysql> show replica status\G
*************************** 1. row ***************************
Replica_IO_State: Waiting for source to send event
Source_Host: mysql-master1
Source_User: slave_sync_user
Source_Port: 3306
Connect_Retry: 60
Source_Log_File: mysql-bin.000003
Read_Source_Log_Pos: 1448
Relay_Log_File: mysql-relay.000002
Relay_Log_Pos: 328
Relay_Source_Log_File: mysql-bin.000003
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
......
8、配置master2和slave2同步
sql
# mysql-master2
[root@localhost ~]# docker exec -it mysql-master2 /bin/bash
bash-5.1# mysql -uroot -p123456
mysql>
mysql> create user 'repl_user'@'%' identified by '123456';
Query OK, 0 rows affected (0.01 sec)
mysql> create user 'slave_sync_user'@'%' identified by '123456';
Query OK, 0 rows affected (0.00 sec)
mysql> grant replication slave on *.* to 'repl_user'@'%';
Query OK, 0 rows affected (0.00 sec)
mysql> grant replication slave on *.* to 'slave_sync_user'@'%';
Query OK, 0 rows affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql> show binary log status;
+------------------+----------+--------------+--------------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+--------------------------+-------------------+
| mysql-bin.000003 | 1447 | | mysql,information_schema | |
+------------------+----------+--------------+--------------------------+-------------------+
1 row in set (0.00 sec)
# mysql-slave2
[root@localhost ~]# docker exec -it mysql-slave2 /bin/bash
bash-5.1# mysql -uroot -p123456
mysql>
mysql> change replication source to \
-> source_host='mysql-master2',
-> source_user='slave_sync_user',
-> source_password='123456',
-> source_log_file='mysql-bin.000003',
-> source_log_pos=1447,
-> get_source_public_key=1;
Query OK, 0 rows affected, 2 warnings (0.01 sec)
mysql> start replica;
Query OK, 0 rows affected (0.03 sec)
mysql> show replica status\G
*************************** 1. row ***************************
Replica_IO_State: Waiting for source to send event
Source_Host: mysql-master2
Source_User: slave_sync_user
Source_Port: 3306
Connect_Retry: 60
Source_Log_File: mysql-bin.000003
Read_Source_Log_Pos: 1447
Relay_Log_File: mysql-relay.000002
Relay_Log_Pos: 328
Relay_Source_Log_File: mysql-bin.000003
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
......
9、配置master1和master2同步
sql
# mysql-master2
mysql> change replication source to \
-> source_host='mysql-master1',
-> source_user='repl_user',
-> source_password='123456',
-> source_log_file='mysql-bin.000003',
-> source_log_pos=1448,
-> get_source_public_key=1;
Query OK, 0 rows affected, 2 warnings (0.01 sec)
mysql> start replica;
Query OK, 0 rows affected (0.04 sec)
mysql> show replica status\G
*************************** 1. row ***************************
Replica_IO_State: Waiting for source to send event
Source_Host: mysql-master1
Source_User: repl_user
Source_Port: 3306
Connect_Retry: 60
Source_Log_File: mysql-bin.000003
Read_Source_Log_Pos: 1448
Relay_Log_File: d5f566847452-relay-bin.000002
Relay_Log_Pos: 328
Relay_Source_Log_File: mysql-bin.000003
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
......
10、配置master2和master1同步
sql
# mysql-master2 上执行
mysql> show binary log status;
+------------------+----------+--------------+--------------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+--------------------------+-------------------+
| mysql-bin.000003 | 1447 | | mysql,information_schema | |
+------------------+----------+--------------+--------------------------+-------------------+
1 row in set (0.00 sec)
# mysql-master1 上执行
mysql> change replication source to \
-> source_host='mysql-master2',
-> source_user='repl_user',
-> source_password='123456',
-> source_log_file='mysql-bin.000003',
-> source_log_pos=1447,
-> get_source_public_key=1;
Query OK, 0 rows affected, 2 warnings (0.01 sec)
mysql> start replica;
Query OK, 0 rows affected (0.03 sec)
mysql> show replica status\G
*************************** 1. row ***************************
Replica_IO_State: Waiting for source to send event
Source_Host: mysql-master2
Source_User: repl_user
Source_Port: 3306
Connect_Retry: 60
Source_Log_File: mysql-bin.000003
Read_Source_Log_Pos: 1447
Relay_Log_File: bdd9cc4ffbb8-relay-bin.000002
Relay_Log_Pos: 328
Relay_Source_Log_File: mysql-bin.000003
Replica_IO_Running: Yes
Replica_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB:
......
11、数据同步测试
1. 在 mysql-master1 上创建库
sql
-- 在 mysql-master1 上创建库
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.00 sec)
mysql> create database mydb;
Query OK, 1 row affected (0.00 sec)
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydb |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
-- mysql-master2 上查看
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydb |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
-- 在 mysql-slave1 上查看
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydb |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.00 sec)
-- 在mysql-slave2 上查看
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mydb |
| mysql |
| performance_schema |
| sys |
+--------------------+
5 rows in set (0.01 sec)
2. 在mysql-master2上创建表
sql
mysql> use mydb;
Database changed
mysql> create table t1(id int);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into t1 values(1),(2),(3);
Query OK, 3 rows affected (0.01 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from t1;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
-- 在mysql-master1上查看
mysql> select * from mydb.t1;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
-- 在mysql-slave1上查看
mysql> select * from mydb.t1;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
-- 在mysql-slave2上查看
mysql> select * from mydb.t1;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.01 sec)
gs: 0
mysql> select * from t1;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
-- 在mysql-master1上查看
mysql> select * from mydb.t1;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
-- 在mysql-slave1上查看
mysql> select * from mydb.t1;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.00 sec)
-- 在mysql-slave2上查看
mysql> select * from mydb.t1;
+------+
| id |
+------+
| 1 |
| 2 |
| 3 |
+------+
3 rows in set (0.01 sec)