Appearance
13|主从复制
前面所有操作都在一台 MySQL 上。但一台机器挂了,数据库就不可用了——单点故障。而且读写都压在一台,查询多了扛不住。
主从复制解决这个——把主库的写操作同步到从库,从库可以分担读、可以做备份、主库挂了还能顶上。本篇讲复制原理、怎么搭主从、复制延迟怎么处理。高可用(自动切换)下一篇讲。
一、复制怎么工作
主库的每次数据变更都记到 binlog。从库去主库拉 binlog,在自己身上回放一遍,数据就和主库一致了:
text
主库写入 → binlog → 从库 IO 线程拉取 → relay log → SQL 线程回放| 对象 | 职责 |
|---|---|
| binlog(主库) | 记录已提交事务的变更 |
| IO 线程(从库) | 连主库、拉 binlog、存为 relay log |
| relay log(从库) | binlog 的本地副本 |
| SQL 线程(从库) | 读 relay log 逐条回放 |
| GTID | 给每个事务一个全局唯一 ID |
GTID vs 传统位点
从库要知道从主库的哪个位置开始拉 binlog。两种方式:
| 方式 | 怎么定位 | 特点 |
|---|---|---|
| 传统 | binlog 文件名 + 位置(position) | 手工维护,切换要记位点 |
| GTID | 全局事务 ID | 自动协商,新环境优先用 GTID |
GTID 给每个事务一个全局唯一 ID,从库自动知道从哪开始、哪些回放过了。切换主从时不用手工算位点,省心。8.0+ 推荐 GTID。
复制是异步的
默认复制是异步的——主库提交后不等从库确认。主库宕机时,最后一部分事务可能还没传到从库,会丢。
半同步复制要求至少一个从库确认收到才返回成功,降低丢失概率,但增加提交延迟。核心数据用半同步,日志类用异步。
二、主库配置
主库要开 binlog 和 GTID:
ini
[mysqld]
server_id=1 # 复制拓扑内唯一
log_bin=/data/mysql/logs/mysql-bin
binlog_format=ROW
gtid_mode=ON
enforce_gtid_consistency=ONserver_id 在整个复制拓扑里必须唯一——主从不能一样。改完重启。确认生效:
sql
SHOW VARIABLES LIKE 'gtid_mode';
SHOW VARIABLES LIKE 'server_id';三、复制账号
从库要连主库拉 binlog,得有专门账号:
sql
CREATE USER 'repl'@'10.0.%' IDENTIFIED BY 'ReplPassword_123!';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'10.0.%';REPLICATION SLAVE 权限让从库能拉 binlog。限制来源 10.0.%——只让内网从库连。
四、从库初始化(含 Clone 插件)
从库要有一份和主库一致的数据起点,不能从空库开始复制(会缺历史数据)。初始化从库有几种方式:
方式一:Clone 插件(8.0.17+,推荐)
MySQL 自带的 Clone 插件,直接从主库克隆数据到从库,比 mysqldump/XtraBackup 快:
sql
-- 从库上装插件
INSTALL PLUGIN clone SONAME 'mysql_clone.so';
-- 配置克隆来源(指向主库)
CLONE INSTANCE FROM 'repl'@'10.0.0.1':3306
IDENTIFIED BY 'ReplPassword_123!';Clone 直接拷贝主库的数据文件到从库,几 GB 到几十 GB 的库几分钟搞定。拷完自动接上复制(GTID 自动同步)。这是 8.0+ 搭从库最快的方式。
限制:只拷 InnoDB 表;拷贝过程中主库的 DDL 会被阻塞。
方式二:mysqldump(小库)
bash
# 主库上导出,带 GTID
mysqldump -uroot -p --all-databases --single-transaction \
--master-data=2 --set-gtid-purged=ON > full.sql
# 导入从库
mysql -uroot -p < full.sql适合几十 GB 以内的小库。大库导入慢。
方式三:XtraBackup
物理备份恢复到从库,适合大库。比 mysqldump 快,但要手动处理 GTID 位点衔接,比 Clone 步骤多。
五、从库启动复制
数据初始化好(Clone 会自动配好,dump/XtraBackup 要手动),启动复制:
sql
CHANGE REPLICATION SOURCE TO
SOURCE_HOST='10.0.0.1',
SOURCE_USER='repl',
SOURCE_PASSWORD='ReplPassword_123!',
SOURCE_AUTO_POSITION=1; -- GTID 自动定位
START REPLICA; -- 8.0+ 用 REPLICA(旧版 START SLAVE)SOURCE_AUTO_POSITION=1 让从库用 GTID 自动定位从哪开始。看复制状态:
sql
SHOW REPLICA STATUS\G关注两个关键:
Replica_IO_Running: Yes— IO 线程在拉 binlogReplica_SQL_Running: Yes— SQL 线程在回放
两个都 Yes 才正常。哪个 No 看下面的 Last_IO_Error 或 Last_SQL_Error 找原因。
六、复制延迟
从库回放比主库写入慢,就会有延迟——主库刚写的数据,从库还没回放到。
sql
-- 看延迟多少秒
SHOW REPLICA STATUS\G
-- 关注 Seconds_Behind_Master延迟的原因和处理:
| 原因 | 处理 |
|---|---|
| 从库机器比主库差 | 升级从库配置 |
| 大事务(一条 SQL 改百万行) | 应用拆小批 |
| 从库被慢查询占用回放线程 | 从库别跑重查询 |
| 网络带宽不够 | 升级网络 |
从库延迟是生产常见问题——读写分离时,刚写入的数据从库查不到,用户会觉得"提交了但没生效"。对一致性敏感的查询走主库,不敏感的走从库。
七、只读保护
从库设为只读,防止误写:
sql
SET GLOBAL super_read_only = ON;从库一旦被写,和主库数据就不一致了,复制可能报错。设只读后,除了复制线程回放,其他写都拒绝。
八、复制异常排查
SHOW REPLICA STATUS 里 IO 或 SQL 线程 No 时:
sql
SHOW REPLICA STATUS\G
-- 看 Last_IO_Error / Last_SQL_Error常见:
- IO 线程 No:网络不通、复制账号密码错、主库 binlog 被清理了(从库要的位点没了)
- SQL 线程 No:回放时冲突(从库已有这行数据、主键冲突)、表结构不一致
binlog 被清理是常见坑——主库 binlog_expire_logs_seconds 设太短,从库延迟太久没拉到的 binlog 被主库删了,从库追不上。生产 binlog 保留时间要够长(至少几天)。
跳过一条出错的事务(确认能跳过时):
sql
STOP REPLICA;
SET GTID_NEXT='uuid:事务号';
BEGIN; COMMIT; -- 注入一个空事务占位
SET GTID_NEXT='AUTOMATIC';
START REPLICA;九、延迟从库
故意让从库比主库慢一段时间(比如 1 小时),作为误删数据的兜底——主库误删了,延迟从库还有 1 小时前的数据:
sql
CHANGE REPLICATION SOURCE TO SOURCE_DELAY=3600;延迟从库不能当实时读用,但误删恢复时是救命稻草。
复制之后
主从复制搭好了,数据能同步了。但主库挂了,从库不会自动变成主库——要人工切换或用高可用组件。下一篇讲 InnoDB Cluster——自动故障切换的高可用方案。