网站访问量上来了、数据库读写压力大,或者担心数据库单独一台机器万一挂了数据丢失——这时候就该给数据库做主从复制(Master-Slave Replication)了。它把主库的数据实时同步到从库:主库负责写入,从库负责读取(读写分离),主库挂了还能随时切到从库。这篇文章从零讲清 MySQL 主从复制的原理、搭建步骤和常见坑。
一、什么是主从复制
MySQL 主从复制就是让一台"从库"实时复制"主库"的数据变化。核心机制是:
- 主库(Master)把所有数据变更记到二进制日志(binlog)
- 从库(Slave)通过 I/O 线程拉取主库的 binlog
- 从库的 SQL 线程把日志里的操作重放到本地
主从复制带来的三大价值:
- 读写分离:主库写、从库读,分摊压力
- 数据备份:从库相当于实时的异地备份
- 高可用:主库故障时,把从库切换为主库,业务不停
二、环境准备
需要两台服务器(或用一台服务器装两个 MySQL 实例,但生产环境建议两台独立机器):
- 主库:IP 192.168.1.10,MySQL 主账号
- 从库:IP 192.168.1.11,MySQL 从账号
两台机都要能互相访问 3306 端口,并已装好 MySQL(版本建议相同,至少主库版本 ≤ 从库版本)。
三、配置主库(Master)
1. 修改主库配置文件 /etc/my.cnf:
[mysqld]
server-id = 1
log-bin = mysql-bin
binlog-format = ROW
expire_logs_days = 7
2. 重启 MySQL:
systemctl restart mysqld
3. 创建同步专用账号:
CREATE USER 'repl'@'192.168.1.%' IDENTIFIED BY '强密码';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.%';
FLUSH PRIVILEGES;
4. 查看主库状态,记录 File 和 Position:
SHOW MASTER STATUS;
记下输出的 File(如 mysql-bin.000001)和 Position(如 154),从库配置要用。
四、初始化从库数据
如果主库已有数据,需要先把数据同步到从库。
1. 锁定主库,导出数据:
FLUSH TABLES WITH READ LOCK; -- 在另一个会话执行,锁表
mysqldump -u root -p --single-transaction --master-data=2 数据库名 > master.sql
2. 解锁主库:
UNLOCK TABLES;
3. 把数据导入从库:
scp master.sql root@192.168.1.11:/root/
mysql -u root -p < master.sql
五、配置从库(Slave)
1. 修改从库配置文件 /etc/my.cnf:
[mysqld]
server-id = 2
log-bin = mysql-bin
relay-log = mysql-relay
read-only = 1
2. 重启从库:
systemctl restart mysqld
3. 连接主库并启动同步:
CHANGE MASTER TO
MASTER_HOST='192.168.1.10',
MASTER_USER='repl',
MASTER_PASSWORD='强密码',
MASTER_LOG_FILE='mysql-bin.000001',
MASTER_LOG_POS=154;
START SLAVE;
六、验证同步是否正常
1. 查看从库状态:
SHOW SLAVE STATUS\G
关键看两个字段,必须都是 YES:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
如果一个是 NO,重点关注错误信息(后面有排查)。
2. 实际测试同步:
在主库建一张测试表并插入数据:
-- 主库
CREATE DATABASE testdb;
USE testdb;
CREATE TABLE tb1(id INT, name VARCHAR(20));
INSERT INTO tb1 VALUES(1, 'hello');
在从库查询(read-only 从库只能读):
-- 从库
USE testdb;
SELECT * FROM tb1; -- 应能看到刚插入的数据
能看到数据,说明主从同步成功。
七、实现读写分离
主从复制完成后,还要让应用"把写放主库、把读放从库",才叫真正的读写分离。常用做法:
- 应用程序配置双数据源:在应用配置里指定写库和读库地址,框架(如 Spring、Django)支持一主多从。
- 中间件(MyCat / ProxySQL):在应用和数据库之间加一层代理,自动路由读写请求。
- MySQL Router:Oracle 官方提供的读写分离路由组件。
对个人站来说,如果只是想要高可用的数据备份,可以先只做主从复制,读写分离等数据量大时再加。
八、故障切换(主从切换)
主库挂了,要把从库临时顶上去:
1. 在从库停止同步并解除只读:
STOP SLAVE;
RESET MASTER;
SET GLOBAL read_only=OFF;
2. 把应用连接地址指向从库 IP。
3. 修好旧主库后,再反过来把它作为新主库的新从库(角色互换)。
(完整的自动故障切换建议用 MHA 或 MySQL InnoDB Cluster,这里只讲手动应急。)
九、常见问题排查
Q1:Slave_IO_Running: Connect 报错?
最常见是 Repl 账号连接不上。检查:
- 主库账号的网络权限是否允许从库 IP(192.168.1.%)
- 服务器防火墙是否放行 3306
- CHANGE MASTER TO 里的密码是否填对
Q2:Slave_IO_Running: No,Slave_SQL_Running 正常?
通常是网络或 binlog 拉取问题。看 Last_IO_Error,常见是主库 binlog 被清理导致拉不到。
Q3:Slave_SQL_Running: No?
一般是 SQL 重放报错(如数据冲突)。看 Last_SQL_Error。如果是误操作,可以手动跳过错误:
STOP SLAVE;
SET GLOBAL sql_slave_skip_counter=1; -- 跳过一条(仅应急)
START SLAVE;
Q4:主从数据不一致?
长期误差可重新初始化从库:重新 mysqldump 主库数据导入从库,再 CHANGE MASTER TO 到新的 binlog 位置。
总结
MySQL 主从复制是数据库高可用的基本功:主库写、从库读、实时备份、故障切换,一套配好网站数据就有了"双保险"。核心步骤是——主库开 binlog 建同步账号、从库配置 server-id 并 CHANGE MASTER TO,最后重点盯 Slave_IO_Running 和 Slave_SQL_Running 两个状态。配合本站的 MySQL 教程和备份教程,你的数据库就稳了。
