一、背景介绍:为什么需要 MySQL 主从复制?
在实际生产环境中,单台 MySQL 服务器存在明显的风险和性能瓶颈:
- 单点故障:一旦服务器宕机,业务将完全中断;
- 性能瓶颈:读写请求全部集中在一台服务器,高并发场景下响应缓慢;
- 数据安全:单节点存储数据,硬件故障可能导致数据丢失。
MySQL 主从复制(Master-Slave Replication)是解决上述问题的经典方案:
- 主库(Master):负责处理所有写操作(增删改),是数据的 “源头”;
- 从库(Slave):通过复制主库的二进制日志(binlog)同步数据,主要处理读操作(查询);
- 核心价值:实现读写分离、数据备份、故障转移,提升系统可用性和性能。
本文基于 CentOS 7 系统和 MySQL 5.7 版本,从零开始拆解主从复制的搭建流程,包含环境准备、安装配置、同步测试和问题排查,适合运维工程师和后端开发人员学习实践。
二、核心关键词解释
- 二进制日志(binlog):主库记录所有数据变更操作的日志文件,是主从复制的 “数据源”;
- 中继日志(relay log):从库接收主库的 binlog 后,先写入中继日志,再逐步执行,避免直接执行 binlog 导致的性能问题;
- server-id:主从服务器的唯一标识(必须不同),用于区分复制拓扑中的节点;
- 复制用户:主库中专门用于从库连接并获取 binlog 的用户,需授予
REPLICATION SLAVE权限; - GTID(全局事务标识符):MySQL 5.6 + 引入的特性,通过全局唯一 ID 标识事务,简化复制配置(本文使用传统的日志文件 + 位置方式)(暂时与本文无关)。
三、环境准备
1. 基础要求
- 两台 CentOS 7 服务器(分别作为主库、从库):硬件配置建议 2 核 4G 以上,确保网络互通;
- 安装相同版本 MySQL(推荐 5.7.x):版本不一致可能导致 binlog 解析错误,本文以 5.7.44 为例;
- 关闭防火墙及 SELinux:避免端口拦截或权限限制(生产环境可按需配置规则,而非直接关闭)。
2. 网络与主机配置
主服务器操作(IP:192.168.238.134)
bash
# 设置主机名(便于识别节点角色,替换为实际域名/名称)
hostnamectl set-hostname master.yourdomain.com
# 配置静态IP(编辑网卡文件,CentOS 7默认网卡为ens33,可通过ip addr查看)
vim /etc/sysconfig/network-scripts/ifcfg-ens33
# 网卡配置内容(根据实际网络环境修改):
BOOTPROTO=static # 静态IP模式(避免DHCP分配导致IP变化)
IPADDR=192.168.238.134 # 主服务器固定IP
PREFIX=24 # 子网掩码前缀(24对应255.255.255.0)
GATEWAY=192.168.238.2 # 网关地址(与路由器/交换机配置一致)
DNS1=114.114.114.114 # DNS服务器(国内常用114或阿里云DNS)
ONBOOT=yes # 开机自动激活网卡
# 重启网络服务使配置生效
systemctl restart network
从服务器操作(IP:192.168.238.133)
bash
# 设置主机名(区分主从节点)
hostnamectl set-hostname slave.yourdomain.com
# 配置静态IP(网卡名称需与主库一致)
vim /etc/sysconfig/network-scripts/ifcfg-ens33
# 网卡配置内容(网关/DNS与主库保持一致):
BOOTPROTO=static
IPADDR=192.168.238.133 # 从服务器固定IP
PREFIX=24
GATEWAY=192.168.238.2 # 与主库同一网关
DNS1=114.114.114.114
ONBOOT=yes
# 重启网络
systemctl restart network
3. 基础环境配置(两台服务器均执行)
bash
# 关闭NetworkManager(避免与network服务冲突,按需选择)
systemctl stop NetworkManager && systemctl disable NetworkManager
# 配置hosts文件(绑定IP与主机名,避免DNS解析问题)
vim /etc/hosts
# 添加内容(替换为实际IP和主机名):
192.168.238.134 master.yourdomain.com
192.168.238.133 slave.yourdomain.com
# 关闭防火墙和SELinux(临时+永久)
systemctl stop firewalld && systemctl disable firewalld # 关闭防火墙
setenforce 0 # 临时关闭SELinux
sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config # 永久关闭SELinux(需重启生效)
# 时间同步(确保主从服务器时间一致,避免复制延迟判断错误)
yum install -y ntp # 安装ntp工具
ntpdate time1.aliyun.com # 同步阿里云NTP服务器时间(生产环境建议用内部NTP)
# 检查3306端口是否被占用(避免端口冲突)
# 方法1:使用netstat(需安装net-tools)
yum install -y net-tools
netstat -tulpn | grep 3306
# 方法2:使用lsof(需安装lsof)
yum install -y lsof
lsof -i :3306
# 若端口被占用,终止占用进程(替换<PID>为实际进程ID)
# kill -9 <PID>
四、安装 MySQL 5.7(主库与从库分别操作)
📌 重要原则:
- 主从服务器必须使用完全相同的 MySQL 版本(本文为 5.7.44)
- 仅通过官方 YUM 仓库 + RPM 安装,严禁混用源码编译版本
- 若系统曾安装过其他版本 MySQL(如
/usr/local/mysql),必须彻底清理
步骤 1:【主库 & 从库】彻底清理旧版 MySQL(关键!)
⚠️ 若跳过此步,极易因残留文件导致服务启动失败或端口冲突!
# 1. 停止所有 MySQL 进程
sudo systemctl stop mysqld 2>/dev/null
sudo pkill -f "/usr/local/mysql" 2>/dev/null
# 2. 卸载已安装的 MySQL RPM 包(如有)
sudo rpm -qa | grep mysql | xargs sudo rpm -e --nodeps 2>/dev/null
# 3. 删除源码安装残留(重点!)
sudo rm -rf /usr/local/mysql
sudo rm -f /usr/bin/mysql*
sudo rm -f /etc/my.cnf
sudo rm -f /etc/init.d/mysqld
# 4. 清理数据与日志目录
sudo rm -rf /var/lib/mysql
sudo rm -rf /var/log/mysqld.log
# 5. 清理用户(可选,避免权限混乱)
sudo userdel -r mysql 2>/dev/null
💡 执行后验证:
which mysql应返回空;ps aux | grep mysqld应无进程。
步骤 2:【主库 & 从库】导入 MySQL 官方 GPG 公钥
🔐 GPG 密钥用于验证 RPM 包真实性,防止安装被篡改的软件包。
# 下载并导入两个官方 GPG 密钥(覆盖新旧签名)
sudo rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql
sudo rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2023
# 验证是否导入成功
rpm -q gpg-pubkey --qf '%{NAME}-%{VERSION}-%{RELEASE}\t%{SUMMARY}\n' | grep -i mysql
✅ 预期输出(示例):
1gpg-pubkey-3a79bd29-63f48c7b gpg(MySQL Release Engineering <mysql-build@oss.oracle.com>)
2gpg-pubkey-859be8d7-63f48c7b gpg(MySQL Package signing key (www.mysql.com) <build@mysql.com>)
❗ 若未导入,后续
yum install可能报错:GPG check FAILED或MISSING KEYS。
步骤 3:【主库 & 从库】配置 MySQL 5.7 YUM 仓库
# 创建仓库文件
sudo tee /etc/yum.repos.d/mysql57-community.repo <<'EOF'
[mysql57-community]
name=MySQL 5.7 Community Server
baseurl=https://repo.mysql.com/yum/mysql-5.7-community/el/7/$basearch/
enabled=1
gpgcheck=1
gpgkey=https://repo.mysql.com/RPM-GPG-KEY-mysql-2023
EOF
# 清理并重建 yum 缓存
sudo yum clean all
sudo yum makecache
✅ 验证仓库可用性:
yum repolist enabled | grep mysql # 应看到 mysql57-community
步骤 4:【主库 & 从库】安装 MySQL 5.7 社区版
# 安装完整套件(包含 server, client, common, libs 等)
sudo yum install -y mysql-community-server
⏳ 安装过程会自动下载以下关键组件:
mysql-community-commonmysql-community-libsmysql-community-clientmysql-community-server
💡 特别注意:
- 不要单独安装
.rpm文件(除非网络受限且能保证依赖完整)- 必须通过
yum install自动解决依赖,否则可能缺失mysql客户端命令
步骤 5:【主库 & 从库】验证安装结果
# 1. 检查服务状态(此时应未启动)
sudo systemctl status mysqld # 应显示 "inactive"
# 2. 检查关键文件是否存在
ls -l /usr/sbin/mysqld # 服务端二进制
ls -l /usr/bin/mysql # 客户端命令(必须存在!)
ls -l /usr/lib/systemd/system/mysqld.service
# 3. 检查预启动脚本(避免 status=203 错误)
ls -l /usr/bin/mysqld_pre_systemd
file /usr/bin/mysqld_pre_systemd # 应为 shell script
❌ 若
/usr/bin/mysql不存在,请重新执行:sudo yum reinstall -y mysql-community-client
步骤 6:【主库 & 从库】初始化并启动 MySQL(首次)
# 创建数据目录(systemd 启动时会自动初始化,但手动更可控)
sudo mkdir -p /var/lib/mysql
sudo chown mysql:mysql /var/lib/mysql
# 启动服务(首次启动会自动生成临时 root 密码)
sudo systemctl start mysqld
# 设置开机自启
sudo systemctl enable mysqld
# 获取临时密码
sudo grep 'temporary password' /var/log/mysqld.log
✅ 示例输出:
2025-12-01T07:40:25.096876Z 1 [Note] A temporary password is generated for root@localhost: lpiRkddQh3;A
步骤 7:【主库 & 从库】运行安全初始化(设置 root 密码)
# 执行安全脚本(按提示操作)
sudo mysql_secure_installation
交互流程建议:
- 输入临时密码 →
lpiRkddQh3;A - 设置新 root 密码 → 必须满足复杂度要求(如
MyNewPass!2025) - 其余选项全部选
Y(移除匿名用户、禁用远程 root、删除 test 库、刷新权限)
🔒 完成后,root 用户只能本地登录,密码已更新。
步骤 8:【主库 & 从库】验证客户端可用性
# 尝试登录(输入刚设置的 root 密码)
mysql -u root -p -e "SELECT VERSION();"
✅ 成功输出:
+-----------+
| VERSION() |
+-----------+
| 5.7.44 |
+-----------+
❌ 若报错
Can't connect to local MySQL server through socket,创建软链接:1sudo ln -s /var/lib/mysql/mysql.sock /tmp/mysql.sock
五、主服务器配置(master)
✅ 前提:MySQL 5.7 已按第四部分完成安装、初始化、root 密码设置,服务正常运行。
1. 修改 /etc/my.cnf 配置(核心步骤)
# 编辑 MySQL 配置文件
sudo vim /etc/my.cnf
替换或追加以下内容(重点新增 server-id 和 log-bin):
[mysqld]
# 基础路径(YUM 安装默认路径)
basedir=/usr
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
pid-file=/var/run/mysqld/mysqld.pid
port=3306
# 网络监听(允许远程连接)
bind-address=0.0.0.0
# 主从复制核心配置(必须!)
server-id=1 # 主库唯一ID,必须为正整数且全局唯一
log-bin=mysql-bin # 开启二进制日志(文件名前缀)
binlog_format=ROW # 推荐 ROW 格式,兼容性好、数据安全
# 字符集(支持 emoji 和多语言)
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
# 日志与安全
log-error=/var/log/mysqld.log
symbolic-links=0
[client]
socket=/var/lib/mysql/mysql.sock
default-character-set=utf8mb4
[mysqld_safe]
log-error=/var/log/mysqld.log
socket=/var/lib/mysql/mysql.sock
💡 说明:
server-id=1:主库标识,从库必须不同(如 100)。log-bin=mysql-bin:开启 binlog,是主从复制的 前提条件。
2. 重启 MySQL 使配置生效
sudo systemctl restart mysqld
sudo systemctl status mysqld # 确认运行正常
3. 验证 binlog 是否启用
mysql -u root -p -e "SHOW VARIABLES LIKE 'log_bin';"
✅ 正确输出:
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| log_bin | ON |
+---------------+-------+
4. 创建复制专用用户
-- 登录 MySQL
mysql -u root -p
-- 创建用户(限制仅从库IP段可连接)
CREATE USER 'repl_user'@'192.168.238.%' IDENTIFIED BY 'YourPassword123!';
-- 授予最小必要权限
GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'192.168.238.%';
-- 刷新权限
FLUSH PRIVILEGES;
EXIT;
🔒 安全建议:生产环境应将
%替换为具体从库 IP,如'repl_user'@'192.168.238.133'。
5. 锁定表并记录 binlog 位置(用于初始化从库)
⚠️ 此步骤仅在使用 物理同步(rsync) 时需要。若使用
mysqldump,可跳过锁表。
-- 登录 MySQL(保持此会话不退出!)
mysql -u root -p
-- 锁定所有表(禁止写入,确保数据一致性)
FLUSH TABLES WITH READ LOCK;
-- 记录当前 binlog 文件名和位置(关键!)
SHOW MASTER STATUS;
✅ 示例输出(请记录实际值):
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000003 | 154 | | | |
+------------------+----------+--------------+------------------+-------------------+
📌 重要:不要关闭此 MySQL 终端!锁会在会话断开时自动释放。
六、从服务器配置(slave)
✅ 前提:MySQL 5.7 已按第四部分完成安装、初始化、root 密码设置,服务正常运行。
1. 修改 /etc/my.cnf 配置
# 编辑配置文件
sudo vim /etc/my.cnf
替换或追加以下内容(修正 basedir,添加从库专属参数):
[mysqld]
# 基础路径(YUM 安装路径为 /usr)
basedir=/usr
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
pid-file=/var/run/mysqld/mysqld.pid
port=3306
# 网络监听
bind-address=0.0.0.0
# 从库核心配置
server-id=100 # 必须与主库不同!
relay-log=/var/lib/mysql/relaylog # 中继日志前缀
read-only=1 # 非 SUPER 用户只读
super-read-only=1 # 包括 root 在内的所有用户只读(MySQL 5.7+)binlog_format=ROW # 与主库一致
log-bin=mysql-bin # 可选(若未来做级联复制则需开启)
expire_logs_days=7 # 自动清理旧日志
# 字符集
character-set-server=utf8mb4
collation-server=utf8mb4_unicode_ci
log-error=/var/log/mysqld.log
symbolic-links=0
[client]
socket=/var/lib/mysql/mysql.sock
default-character-set=utf8mb4
[mysqld_safe]
log-error=/var/log/mysqld.log
socket=/var/lib/mysql/mysql.sock
2. 同步主库数据(两种安全方式任选其一)
✅ 方式 A:物理同步(适用于大库,需短暂只读)
在主库操作(保持上一步的 MySQL 锁会话不退出)
# 另开一个 SSH 窗口,执行 rsync(不要停主库服务!)
sudo rsync -av /var/lib/mysql/ root@192.168.238.133:/var/lib/mysql/
在从库操作
# 修复权限
sudo chown -R mysql:mysql /var/lib/mysql
# 删除 auto.cnf(避免 UUID 冲突)
sudo rm -f /var/lib/mysql/auto.cnf
回到主库的 MySQL 会话
-- 记录 SHOW MASTER STATUS 的结果后,解锁
UNLOCK TABLES;
EXIT;
✅ 方式 B:逻辑备份(适用于小库,不停写)
在主库操作
# 导出全量数据(自动包含 binlog 位置)
mysqldump --all-databases --single-transaction --master-data=2 \
--routines --triggers --events -uroot -p > /tmp/full.sql
# 传输到从库
scp /tmp/full.sql root@192.168.238.133:/tmp/
在从库操作
# 停止 MySQL(因要导入全量数据)
sudo systemctl stop mysqld
# 清空现有数据(可选)
sudo rm -rf /var/lib/mysql/*
# 启动服务(重建基础结构)
sudo systemctl start mysqld
# 导入数据(会自动应用 binlog 位置)
mysql -u root -p < /tmp/full.sql
3. 启动从库并验证
# 如果使用方式 A,需重启从库
sudo systemctl restart mysqld
# 验证服务状态
sudo systemctl status mysqld
七、配置主从同步(从服务器执行)
✅ 前提:已记录主库的
File和Position(如mysql-bin.000003,154)
-- 登录从库 MySQL
mysql -u root -p
-- 配置主库连接信息
CHANGE MASTER TO
MASTER_HOST='192.168.238.134',
MASTER_USER='repl_user',
MASTER_PASSWORD='YourPassword123!',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000003', -- 替换为你记录的实际值
MASTER_LOG_POS=154; -- 替换为你记录的实际值
-- 启动复制
START SLAVE;
-- 检查状态
SHOW SLAVE STATUS\G
需满足以下条件(同步成功):
Slave_IO_Running: Yes:从库 IO 线程正常(成功连接主库并获取 binlog);Slave_SQL_Running: Yes:从库 SQL 线程正常(成功执行中继日志);Seconds_Behind_Master: 0:从库无延迟(实时同步)。
3. 主服务器解锁表(同步配置完成后)
回到主库 MySQL 终端,执行以下命令释放表锁,允许写入操作:
sql
UNLOCK TABLES;
八、测试主从复制
1. 主服务器操作(写入数据)
sql
-- 创建测试数据库
CREATE DATABASE test_replication;
USE test_replication;
-- 创建测试表
CREATE TABLE test_table (
id INT PRIMARY KEY,
name VARCHAR(50),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- 插入测试数据
INSERT INTO test_table (id, name) VALUES (1, '测试数据1');
INSERT INTO test_table (id, name) VALUES (2, '测试数据2');
-- 确认数据插入
SELECT * FROM test_table;
2. 从服务器验证(同步数据)
sql
-- 查看是否同步了测试数据库
SHOW DATABASES; # 应包含test_replication
-- 查看同步的数据
USE test_replication;
SELECT * FROM test_table; # 应显示主库插入的两条数据
若从库能查询到主库插入的数据,说明主从复制配置成功!
九、常见问题排查
1. 复制错误处理(从库执行)
sql
-- 停止复制进程
STOP SLAVE;
-- 重置复制配置(谨慎使用,会清除现有同步状态)
RESET SLAVE;
-- 重新配置主从连接(参考步骤七第1点)
CHANGE MASTER TO ...;
-- 重启复制进程
START SLAVE;
2. 网络和权限检查
bash
# 检查主从网络连通性(从库ping主库)
ping 192.168.238.134
# 检查主库3306端口是否可访问(从库telnet测试)
telnet 192.168.238.134 3306
# 开放防火墙端口(若未关闭防火墙)
firewall-cmd --add-port=3306/tcp --permanent
firewall-cmd --reload
# 检查复制用户权限(主库执行)
mysql -uroot -p -e "SHOW GRANTS FOR 'repl_user'@'192.168.238.133';"
3. 端口占用问题
bash
# 检查3306端口占用情况
netstat -tulpn | grep 3306
# 终止占用进程(替换<PID>为实际进程ID)
kill -9 $(lsof -t -i:3306)
# 重启MySQL
service mysqld start
4. 日志查看(排查问题的关键)
- 主库二进制日志:
/var/lib/mysql/mysql-bin.xxxxxx(通过SHOW BINLOG EVENTS IN 'binlog.000003'查看内容); - 错误日志:
/var/log/mysqld.log(包含启动失败、复制错误等关键信息); - 从库中继日志:
/var/lib/mysql/relaylog.xxxxxx(通过SHOW RELAYLOG EVENTS查看内容)。
十、总结
MySQL 主从复制的核心是主库记录 binlog→从库 IO 线程获取 binlog→SQL 线程执行 binlog,搭建过程需注意以下关键点:
- 主从服务器
server-id必须唯一,主库需开启log-bin; - 复制用户需授予最小权限(
REPLICATION SLAVE),并限制访问 IP; - 数据同步时需锁定主库表,确保一致性;
- 验证同步状态时重点关注
Slave_IO_Running和Slave_SQL_Running是否均为Yes。
通过本文的步骤,你可以快速搭建一套稳定的 MySQL 主从复制环境,实现读写分离和数据备份。生产环境中还可结合keepalived实现主从自动切换,进一步提升系统可用性。
3196

被折叠的 条评论
为什么被折叠?



