Mysql

关注公众号 jb51net

关闭
首页 > 数据库 > Mysql > MySQL主从复制与高可用搭建教程

MySQL主从复制与高可用搭建教程:从原理到实战的完整指南

作者:阿狗童鞋

还在为MySQL单点故障和数据丢失发愁?本文从主从复制原理讲起,手把手教你搭建读写分离和半同步复制,对比MHA、Orchestrator等高可用方案,并附常见坑速查表,掌握这些,让你的数据库挂了一台系统还在跑,数据不丢、业务不中断

单库单机是"能用",主从复制是"可靠",高可用是"挂了一台系统还在跑"。MySQL 主从复制与高可用是 DBA 和后端开发的必备技能,也是 CSDN 数据库板块最热门的话题。

本文从为什么要主从讲起,覆盖复制原理、搭建配置、读写分离、半同步复制、高可用方案(MHA/Orchestrator/Keepalived)、常见坑。

一、为什么需要主从复制

1.1 单库的问题

单库单机:
- 读多写少的业务 → 一台库扛不住读压力
- 主库挂了 → 整个系统不可用
- 备份/恢复期间 → 业务停顿

1.2 主从复制的价值

价值说明
读写分离主库写、从库读,扩展读能力
高可用主库挂 → 从库提升为主库
数据备份从库可作为备份源
灾备跨机房/跨地域从库

二、复制原理(必考)

2.1 复制流程

主库:写操作 → Binlog(二进制日志)
从库:IO 线程拉取 Binlog → 写入中继日志(Relay Log)
      SQL 线程回放 Relay Log → 应用到从库数据
主库                   从库
写入 → Binlog ──(IO线程拉取)──→ Relay Log ──(SQL线程回放)──→ 应用

2.2 三种复制方式

方式同步性一致性
异步复制主库不等待从库可能丢失(主挂时)
半同步复制至少一个从库确认主挂不丢已确认事务
组复制(Group Replication)多数派确认强一致(MySQL 8)

2.3 Binlog 格式

STATEMENT:记录 SQL(占用小,主从数据可能不一致)
ROW:记录行变更(占用大,最安全,默认推荐)
MIXED:混合

推荐 ROW——数据一致且能精确恢复。

三、搭建主从复制(实战)

3.1 环境准备

主库:192.168.1.10
从库:192.168.1.11
MySQL 版本:8.0(两边版本一致)

3.2 主库配置

# my.cnf(主库)
[mysqld]
server-id = 1                  # 唯一
log-bin = mysql-bin            # 开启 Binlog
binlog-format = ROW            # ROW 格式
binlog-expire-logs-seconds = 2592000    # 保留 30 天
# 重启主库
systemctl restart mysqld

3.3 主库创建复制账号

-- 登录主库创建复制用户
CREATE USER 'repl'@'192.168.1.%' IDENTIFIED BY 'Repl@123456';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.%';
FLUSH PRIVILEGES;

-- 查看当前 Binlog 位置(从库要用)
SHOW MASTER STATUS;
-- +------------------+----------+------------------+
-- | File             | Position | Binlog_Do_DB    |
-- +------------------+----------+------------------+
-- | mysql-bin.000001 |     154  |                  |
-- +------------------+----------+------------------+

3.4 从库配置

# my.cnf(从库)
[mysqld]
server-id = 2                  # 不能和主库一样
read_only = 1                  # 从库只读(防误写)
systemctl restart mysqld

3.5 从库启动复制

-- 登录从库
CHANGE MASTER TO
  MASTER_HOST='192.168.1.10',
  MASTER_USER='repl',
  MASTER_PASSWORD='Repl@123456',
  MASTER_LOG_FILE='mysql-bin.000001',
  MASTER_LOG_POS=154;

START SLAVE;

-- 查看复制状态(重点!)
SHOW SLAVE STATUS\G;
-- 关键字段:
-- Slave_IO_Running: Yes     ← IO 线程正常
-- Slave_SQL_Running: Yes    ← SQL 线程正常
-- Seconds_Behind_Master: 0  ← 延迟 0 秒
-- Last_Error:               ← 最近错误(为空)

3.6 验证

-- 主库建表插数
CREATE DATABASE test;
USE test;
CREATE TABLE t (id INT PRIMARY KEY, name VARCHAR(20));
INSERT INTO t VALUES (1, '张三');

-- 从库查询(数据已同步)
USE test;
SELECT * FROM t;    -- 能查到 → 复制成功

验证三要素:Slave_IO_Running=Yes、Slave_SQL_Running=Yes、Seconds_Behind_Master=0。

四、读写分离(应用层)

4.1 方案选择

方案方式特点
应用层(推荐)框架多数据源灵活可控
中间件(ProxySQL/MyCat)独立代理对应用透明
客户端插件(ShardingSphere)数据源路由集成度高

4.2 Spring Boot + ShardingSphere 读写分离

<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
    <version>5.4.0</version>
</dependency>
spring:
  shardingsphere:
    datasource:
      names: master,slave0
      master:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://192.168.1.10:3306/mall
        username: root
        password: 123456
      slave0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://192.168.1.11:3306/mall
        username: root
        password: 123456
    rules:
      readwrite-splitting:
        data-sources:
          ds:
            write-data-source-name: master
            read-data-source-names:
              - slave0
    props:
      sql-show: true
// 业务代码不用改!注解控制路由
@Transactional          // 事务内走主库(防止读不到刚写的数据)
public void createOrder(Order order) {
    orderMapper.insert(order);
}

public Order getOrder(Long id) {
    return orderMapper.selectById(id);   // 默认走从库
}

关键:强一致读必须走主库(或主从延迟容忍度内再走从库)——用 @Transactional 或强制路由注解。

五、主从延迟(最核心的坑)

5.1 为什么有延迟

从库 SQL 线程回放是单线程的(MySQL 8 之前)
主库并发写入 → 从库串行回放 → 追不上 → 延迟累积

5.2 监控延迟

SHOW SLAVE STATUS\G;
-- Seconds_Behind_Master: 5    ← 延迟秒数
-- 持续增长说明从库跟不上

5.3 解决延迟

手段说明
并行复制slave-parallel-workers=8(MySQL 8 默认开启)
减少大事务一次别更新太多行
索引优化回放慢多因更新语句全表扫
读写分离兜底延迟敏感场景强制走主库
分库分表单库压力下降延迟自然降
# 从库并行复制配置
[mysqld]
slave-parallel-type = LOGICAL_CLOCK
slave-parallel-workers = 8

六、半同步复制(防丢数据)

6.1 异步的问题

异步复制:主库写完 Binlog 就返回成功
→ 主库突然宕机 → 新主库没有最后几条事务 → 数据丢失

6.2 半同步原理

主库事务提交 → 等待至少一个从库确认收到 → 才返回成功

实现:MySQL 半同步插件(semisync)。

# 主库加载插件
INSTALL PLUGIN rpl_semi_sync_source SONAME 'semisync_source.so';
SET GLOBAL rpl_semi_sync_source_enabled = 1;

# 从库
INSTALL PLUGIN rpl_semi_sync_replica SONAME 'semisync_replica.so';
SET GLOBAL rpl_semi_sync_replica_enabled = 1;

# 重启复制
STOP SLAVE; START SLAVE;

效果:主库挂了,已确认事务不丢(在从库有副本);代价是写入性能下降(等一个从库确认)。

MySQL 8.0.26+:原生支持 rpl_semi_sync_source_enabled,配置更简单。

七、高可用方案

7.1 目标

主库宕机 → 自动选新主 → 应用无感知(秒级切换)→ 数据不丢

7.2 主流方案对比

方案原理切换速度特点
MHA脚本管理主从,自动提升10-30 秒经典、稳定
Orchestrator拓扑管理 + 自动故障转移秒级现代、推荐
Keepalived + 主从VIP 漂移秒级简单但可能脑裂
MGR(组复制)多数派协议自动MySQL 8 原生

7.3 Orchestrator(现代推荐)

# 安装 orchestrator(管理所有 MySQL 实例)
docker run -d \
  --name orchestrator \
  -p 3000:3000 \
  -v /etc/orchestrator.conf.json:/etc/orchestrator.conf.json \
  orchestrator/orchestrator

# 界面:http://localhost:3000
# 功能:拓扑可视化、一键切换、自动故障转移
// orchestrator.conf.json 关键配置
{
  "MySQLTopologyUser": "orc",
  "MySQLTopologyPassword": "orc123456",
  "DetectClusterAliasQuery": "SELECT @@hostname",
  "RecoverMasterCandidateServers": ["192.168.1.11"],
  "PreventCrossDataCenterMasterFailover": false
}

切换流程(Orchestrator 自动):

主库宕机 → 检测失败 → 选出候选从库
→ 补拉缺失 Binlog → 提升为新主 → 其他从库重指新主

7.4 Keepalived + VIP(简单版)

# 主备各装 keepalived,虚拟 IP 在主备间漂移
# vrrp_instance 配置:
# 主库:state MASTER
# 从库:state BACKUP
# 应用只连虚拟 IP,主挂 VIP 自动漂到从库
# 主库检查脚本(MySQL 挂了就降级)
killall -0 mysqld || exit 1

注意:Keepalived 方案切换快但可能脑裂(两个都认为自己是主)——必须配 MySQL 层面的保护和数据一致性兜底。

八、常见坑速查

现象原因解决
Slave_IO_Running: No网络/账号/位置错检查主从连通、账号权限、Binlog 位置
Slave_SQL_Running: No回放出错(主从数据冲突)看 Last_Error,跳过/重建从库
从库数据不一致STATEMENT 格式换 ROW 格式重建从库
延迟持续增长大事务/单线程回放并行复制 + 拆大事务
主挂后丢数据异步复制半同步 + 高可用方案
从库被写坏没开只读read_only = 1
切换后数据不一致没补 BinlogOrchestrator 自动补/手动补
两边版本不一致协议不兼容升级从库到同版本

总结

MySQL 高可用的落地主线:主从复制(Binlog → Relay Log → 回放)→ 读写分离(应用层/中间件)→ 半同步(防丢)→ 自动故障转移(Orchestrator/MHA)。

实践要点:ROW 格式、从库只读、监控延迟三要素、延迟敏感读走主库。

记住一句话:复制保数据可用,切换保系统可用,两者配合才是高可用。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文