MySQL主从同步原理
主从同步通过主库(Master)记录变更日志,从库(Slave)读取并重放日志实现数据复制,保障数据冗余和高可用性。
主从架构示意
表格:MySQL主从复制组件及作用
组件 | 作用 |
---|---|
Binary Log | 主库记录所有数据变更的日志,是主从同步的核心。 |
I/O线程 | 从库的线程,负责从主库获取Binary Log并写入Relay Log。 |
SQL线程 | 从库的线程,负责读取Relay Log并执行其中的SQL语句。 |
Relay Log | 从库本地存储主库Binary Log的中间日志,用于SQL线程重放。 |
MySQL主从复制流程
- 主节点MySQL接收到数据变更时,将事务写入Binary Log。
- Binary Log通过多线程机制被读取并发送到从节点。
- 从节点的I/O线程接收Binary Log内容,写入本地Relay Log。
- 从节点的SQL线程读取Relay Log中的SQL语句并执行,最终将数据变更应用到从节点MySQL。
主从同步方式对比
同步方式 | 原理 | 优点 | 缺点 |
---|---|---|---|
异步复制 (Asynchronous) | 主库提交事务后立即返回,不等待从库确认 | 性能高,延迟低 | 从库可能滞后,数据一致性较差 |
半同步复制 (Semi-Synchronous) | 主库提交事务后,至少等待一个从库确认接收日志 | 数据一致性更好 | 性能略有下降 |
全同步复制 (Synchronous) | 主库提交事务后,等待所有从库确认接收日志 | 数据一致性最好 | 性能较低 |
增强半同步复制 (Rising-Semi-Sync) | 对半同步复制的改进,原理类似,主要解决幻读问题 | 数据一致性更好,解决幻读问题 | 性能略有下降 |
部署:
编辑Master目录下的Dockerfile:
FROM mysql:5.7.36
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
编辑slave目录下的Dockerfile:
FROM mysql:5.7.36
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
COPY ./slave/slave.sql /docker-entrypoint-initdb.d
编辑slave.sql
change master to master_host='mysql-master',
master_user='root',master_password='root',master_port=3306;
start slave;
在主目录mysql下创建docker-compose.yml文件进行编辑:
name: mysql
services:
mysql-master:
build:
context: ./
dockerfile: ./master/Dockerfile
image: mysqlmaster:1.0
restart: always
container_name: mysql-master
volumes:
- ./mastervarlib:/var/lib/mysql
ports:
- 8080:3306
environment:
MYSQL_ROOT_PASSWORD: root
privileged: true
command: ['--server-id=1',
'--log-bin=master-bin',
'--binlog-ignore-db=mysql',
'--binlog_cache_size=256M',
'--binlog_format=mixed',
'--lower_case_table_names=1',
'--character-set-server=utf8',
'--collation-server=utf8_general_ci']
mysql-slave:
build:
context: ./
dockerfile: ./slave/Dockerfile
image: mysqlslave:1.0
restart: always
container_name: mysql-slave
volumes:
- ./slavevarlib:/var/lib/mysql
ports:
- 8081:3306
environment:
MYSQL_ROOT_PASSWORD: root
privileged: true
command: ['--server-id=2',
'--relay_log=slave-relay',
'--lower_case_table_names=1',
'--character-set-server=utf8',
'--collation-server=utf8_general_ci']
depends_on:
- mysql-master
mysql-slave2:
build:
context: ./
dockerfile: ./slave/Dockerfile
image: mysqlslave:1.0
restart: always
container_name: mysql-slave2
volumes:
- ./slavevarlib2:/var/lib/mysql
ports:
- 8082:3306
environment:
MYSQL_ROOT_PASSWORD: root
privileged: true
command: ['--server-id=3',
'--relay_log=slave-relay',
'--lower_case_table_names=1',
'--character-set-server=utf8',
'--collation-server=utf8_general_ci']
depends_on:
- mysql-master
mysql-master:
restart:指定容器的重启策略。
always 表示无论容器因何种原因退出,Docker 都会尝试重新启动它。
volume:用于将主机目录挂载到容器中
./mastervarlib 是主机上的目录,/var/lib/mysql 是容器中 MySQL 默认的数据存储路径。
作用:将 MySQL 的数据存储在主机上,确保容器重启或删除后数据不会丢失。
envirnment:设置环境变量
MYSQL_ROOT_PASSWORD: root 指定 MySQL 的 root 用户密码为 root。
privileged:设置容器以特权模式运行
command:
设置 MySQL 服务器的唯一 ID,主从复制中必须指定,且每个服务器的 ID 必须唯一
启用二进制日志(Binary Log),日志文件前缀为 master-bin,记录主库的写操作
指定忽略mysql系统数据库的二进制日志记录
设置二进制日志缓存的大小为 256MB
设置二进制日志的格式为 mixed
设置表名不区分大小写
设置服务器的默认字符集为 utf8
设置服务器的默认排序规则为 utf8_general_ci
mysql-slave1:
depends-on:指定服务之间的依赖关系
mysql-slave 服务依赖于 mysql-master 服务
确保在启动 mysql-slave 之前,mysql-master 服务已经启动
启动:
docker compose up -d
查看主从复制状态的命令
MySQL命令来检查主从复制状态。以下是完整的操作流程:
docker exec -it mysql-slave mysql -uroot -proot -e "SHOW SLAVE STATUS\G"
这条命令会直接输出从库的复制状态。关键字段包括:
Slave_IO_Running
(IO线程状态)Slave_SQL_Running
(SQL线程状态)Seconds_Behind_Master
(主从延迟秒数)Last_IO_Error
/Last_SQL_Error
(错误信息)
核对以下关键指标:
- 两个线程必须显示为
Yes
:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master
应为0或较小的数值Master_Log_File
与Read_Master_Log_Pos
应与主库的SHOW MASTER STATUS
输出匹配
测试:
在主库上:
docker exec -it mysql-master bash
create database test;
mysql> use test;
Database changed
mysql> create table users(sno int,sname varchar(20));
Query OK, 0 rows affected (0.03 sec)
mysql> insert into users values (1,'xixi');
Query OK, 1 row affected (0.02 sec)
mysql> insert into users values (2,'haha');
Query OK, 1 row affected (0.01 sec)
在从库上:
mysql> use test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+----------------+
| Tables_in_test |
+----------------+
| users |
+----------------+
1 row in set (0.00 sec)
mysql> select * from users;
+------+-------+
| sno | sname |
+------+-------+
| 1 | xixi |
| 2 | haha |
+------+-------+
2 rows in set (0.00 sec)