1.主从复制(Replication)的原理与配置
1.1 核心工作原理
MySQL 主从复制是基于二进制日志 (Binary Log) 的异步数据同步机制,其核心流程分为三步:
用户写入 → 主库 Binlog → 从库 I/O 线程 → Relay Log → 从库 SQL 线程 → 从库数据
详细工作流程:
⭐️主库记录:主库将所有数据变更(INSERT/UPDATE/DELETE)以事件形式写入 binlog
⭐️从库拉取:从库的 I/O 线程连接主库,请求 binlog 事件并写入中继日志(Relay Log)
⭐️从库重放:SQL 线程读取 Relay Log,在从库上按顺序重放这些事件,实现数据同步
数据一致性保障:
⭐️异步复制(默认):主库提交事务后立即返回,不等待从库确认,存在数据丢失风险
⭐️半同步复制:主库等待至少一个从库接收 binlog 后才返回,平衡性能与一致性
⭐️GTID 复制:为每个事务分配全局唯一 ID,从库通过 GTID 判断已执行事务,简化故障切换
1.2 生产环境配置实战
主库配置(my.cnf)
bash
[mysqld]
server-id = 1 # 集群内唯一,1-2^32-1
log_bin = mysql-bin # 开启 binlog,指定文件名前缀
binlog_format = ROW # 推荐 ROW 模式,避免主从不一致
gtid-mode = ON # 启用 GTID
enforce-gtid-consistency = ON # 强制 GTID 一致性
sync_binlog = 1 # 每次事务提交刷盘,保证持久性
innodb_flush_log_at_trx_commit = 1 # Redo Log 同步刷盘
从库配置(my.cnf)
bash
[mysqld]
server-id = 2 # 必须唯一
relay_log = relay-bin # 中继日志
read_only = 1 # 设置只读,防止误写
super_read_only = 1 # 连 root 也无法写入(MySQL 5.7+)
slave_parallel_type = LOGICAL_CLOCK # 并行复制类型
slave_parallel_workers = 8 # 并行线程数,建议 CPU 核心数
创建复制用户
sql
-- 主库执行
CREATE USER 'repl'@'192.168.1.%' IDENTIFIED BY 'StrongPassword123!';
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'192.168.1.%';
FLUSH PRIVILEGES;
从库初始化与启动
sql
-- 使用 xtrabackup 进行热备份(避免锁表)
-- xtrabackup --backup --user=root --target-dir=/data/backup
-- 从库配置主从关系(GTID 方式)
CHANGE MASTER TO
MASTER_HOST='192.168.1.100',
MASTER_USER='repl',
MASTER_PASSWORD='StrongPassword123!',
MASTER_AUTO_POSITION=1; -- GTID 自动定位
START SLAVE;
-- 验证状态
SHOW SLAVE STATUS\G
-- 关键字段:Slave_IO_Running=Yes, Slave_SQL_Running=Yes, Seconds_Behind_Master=0
1.3 复制延迟优化
| 优化手段 | 配置参数 | 效果 |
|---|---|---|
| 并行复制 | slave_parallel_workers=8 |
从库重放速度提升 3-5 倍 |
| 增大日志 | innodb_log_file_size=2G |
减少 Checkpoint,降低延迟 |
| 半同步 | rpl_semi_sync_master_timeout=1000 |
数据一致性提升,写入延迟增加 10-20ms |
| 网络优化 | 主从同机房/同可用区 | 网络延迟从 50ms 降至 <1ms |
读写分离的实现方法
2.1 实现方式对比
| 实现方式 | 架构 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 应用层路由 | 代码硬编码 | 性能最好,无中间件损耗 | 开发成本高,维护困难 | 小型项目,团队技术强 |
| 中间件代理 | ProxySQL/MyCat | 对应用透明,功能强大 | 引入新节点,有性能损耗 | 中大型项目,主流方案 |
| 负载均衡器 | LVS/HaProxy | 四层转发,性能高 | 无法解析 SQL,路由不精确 | 简单读写分离,无复杂规则 |
2.2 应用层实现示例(Spring Boot)
java
@Configuration
public class DataSourceConfig {
@Bean
public DataSource masterDataSource() {
// 主库数据源
return DataSourceBuilder.create()
.url("jdbc:mysql://master-host:3306/db")
.build();
}
@Bean
public DataSource slaveDataSource() {
// 从库数据源(可配多个做负载均衡)
return DataSourceBuilder.create()
.url("jdbc:mysql://slave-host:3306/db")
.build();
}
@Bean
public DataSource routingDataSource() {
return new DynamicDataSource(masterDataSource(), slaveDataSource());
}
}
// 动态数据源路由
public class DynamicDataSource extends AbstractRoutingDataSource {
@Override
protected Object determineCurrentLookupKey() {
return DataSourceContextHolder.isMaster() ? "master" : "slave";
}
}
// 注解实现
@Master // 写操作走主库
public void createOrder(Order order) { ... }
@Slave // 读操作走从库
public List<Order> getOrders(Long userId) { ... }
2.3 中间件 ProxySQL 配置
sql
-- 1. 添加后端服务器
INSERT INTO mysql_servers (hostgroup_id, hostname, port) VALUES
(10, '192.168.1.100', 3306), -- 写组(主库)
(20, '192.168.1.101', 3306), -- 读组(从库1)
(20, '192.168.1.102', 3306); -- 读组(从库2)
-- 2. 配置监控用户
UPDATE global_variables SET variable_value='monitor' WHERE variable_name='mysql-monitor_username';
UPDATE global_variables SET variable_value='monitor123' WHERE variable_name='mysql-monitor_password';
-- 3. 设置路由规则(读写分离)
INSERT INTO mysql_query_rules (rule_id, active, match_pattern, destination_hostgroup, apply) VALUES
(1, 1, '^SELECT.*FOR UPDATE$', 10, 1), -- 显式锁走主库
(2, 1, '^SELECT', 20, 1), -- SELECT 走从库
(3, 1, '.*', 10, 1); -- 其他走主库
LOAD MYSQL VARIABLES TO RUNTIME;
LOAD MYSQL SERVERS TO RUNTIME;
LOAD MYSQL QUERY RULES TO RUNTIME;
2.4 数据一致性挑战与解决方案
问题 :主从延迟导致读不到最新数据(如刚注册的用户无法登录)
解决方案 :
⭐️关键读走主库:用户注册后 3 秒内,所有读请求强制走主库
⭐️半同步复制:对一致性要求高的业务启用半同步
⭐️GTID 等待:从库查询前等待特定 GTID 执行完成(WAIT_FOR_EXECUTED_GTID_SET)
⭐️缓存补偿:写入后同步更新缓存,读请求优先读缓存
java
// 关键读走主库示例
public User getUserImmediately(Long userId) {
// 刚写入的数据,强制走主库
DataSourceContextHolder.setMaster();
return userMapper.selectById(userId);
}
3.集群方案
3.1 MySQL Cluster(NDB)
架构特点:
⭐️Shared-Nothing:数据分片存储在多个数据节点,自动分片
⭐️内存存储:数据默认存储在内存中,支持持久化检查点
⭐️多主写入:任意节点可接受写入,自动同步
⭐️高可用:数据节点自动故障转移,99.999% 可用性
局限:
⭐️复杂度高:需管理 SQL 节点、数据节点、管理节点
⭐️性能瓶颈:网络延迟影响大,不适合复杂 JOIN
⭐️生态较差:社区支持少,企业级应用较少
**适用场景:**电信计费、实时交易系统
3.2 Galera Cluster
架构特点:
⭐️同步多主复制:基于 Certification-based Replication,所有节点数据强一致
⭐️无延迟复制:事务在所有节点提交后才返回,保证一致性
⭐️自动成员管理:节点自动加入/剔除,无需人工干预
配置示例:
bash
# my.cnf 配置 (Percona XtraDB Cluster)
wsrep_provider=/usr/lib/libgalera_smm.so
wsrep_cluster_name=pxc-cluster
wsrep_cluster_address=gcomm://192.168.1.101,192.168.1.102,192.168.1.103
wsrep_node_address=192.168.1.101
wsrep_sst_method=xtrabackup-v2 # 状态快照传输方式
局限:
⭐️写入性能差:事务需所有节点确认,网络延迟影响大
⭐️脑裂风险:网络分区时可能产生脑裂
⭐️锁冲突:多节点同时写入同一行数据易冲突
⭐️适用场景:小规模多活架构,对一致性要求极高的场景
3.3 MySQL InnoDB Cluster(官方推荐)
架构组件:
⭐️MySQL Group Replication:基于 Paxos 协议的组复制,保证数据一致性
⭐️MySQL Router:轻量级中间件,自动故障转移
⭐️MySQL Shell:管理工具,一键部署集群
部署步骤:
bash
# 1. 使用 MySQL Shell 初始化集群
mysqlsh --uri root@mysql1:3306
dba.createCluster('prodCluster')
# 2. 添加节点
cluster.addInstance('root@mysql2:3306')
cluster.addInstance('root@mysql3:3306')
# 3. 检查状态
cluster.status()
# 输出:所有节点 ONLINE,单主模式(PRIMARY/SECONDARY)
优势:
⭐️官方支持:Oracle 官方维护,稳定性有保障
⭐️自动故障转移:主库宕机后自动选举新主库(< 30 秒)
⭐️MySQL 原生:无需额外安装 Galera 库
⭐️读写分离:MySQL Router 自动路由读写请求
局限:
⭐️版本要求高:需 MySQL 5.7.17+ 或 8.0+
⭐️网络要求:RTT < 100ms,不适合跨地域部署
⭐️写入限制:单主模式下仅主库可写,多主模式有冲突风险
4.分库分表策略与中间件选择
4.1 分库分表策略
垂直拆分(按业务)
sql
-- 拆分前:所有业务在一张库
db_ecommerce: users, orders, products, payments, logs
-- 拆分后:按业务模块拆分
db_user: users, user_profiles, user_addresses
db_order: orders, order_items, order_logs
db_product: products, product_skus, product_reviews
适用场景 :微服务架构,业务边界清晰
优点 :业务解耦,不同库可独立优化
缺点 :跨库 JOIN 需应用层处理,分布式事务复杂
水平拆分(按数据)
范围分片(Range Sharding):
sql
-- 按用户 ID 范围分片
db_0: user_id 1-1000000
db_1: user_id 1000001-2000000
db_2: user_id 2000001-3000000
优点 :扩展简单,数据迁移方便
缺点 :热点问题(新注册用户集中在最新分片)
哈希分片(Hash Sharding) :
java
// 一致性哈希算法
int shardIndex = hash(userId) % 4; // 4 个分片
优点 :数据分布均匀,无热点
缺点 :扩容需重新哈希,数据迁移成本高
4.2 中间件对比选型
| 中间件 | 架构 | 优点 | 缺点 | 推荐场景 |
|---|---|---|---|---|
| ShardingSphere | Java 代理/增强 | 功能最全,社区活跃,支持分片/读写分离/加密 | 配置复杂 | 大型企业,Java 生态 |
| MyCAT 2.0 | C++ 代理 | 性能高,支持异构数据库 | 社区活跃度下降 | 遗留系统,MySQL 专属 |
| ProxySQL | C++ 代理 | 轻量级,读写分离优秀 | 分片能力弱 | 简单分片,高性能需求 |
| Vitess | Go 微服务 | YouTube 出品,云原生 | 运维复杂 | 超大规模,Kubernetes 环境 |
4.3 ShardingSphere 配置示例
yaml
# application.yml (Spring Boot)
spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.1.101:3306/db0
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://192.168.1.102:3306/db1
rules:
sharding:
tables:
orders:
actual-data-nodes: ds$->{0..1}.orders_$->{0..1} # ds0.orders_0, ds0.orders_1, ds1.orders_0, ds1.orders_1
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: order-id-mod
key-generate-strategy:
column: order_id
key-generator-name: snowflake
sharding-algorithms:
order-id-mod:
type: MOD
props:
sharding-count: 4 # 4 张分片表
key-generators:
snowflake:
type: SNOWFLAKE
4.4 分库分表避坑指南
必须考虑的问题:
⭐️分布式事务:使用 Seata 或 XA 事务,但性能损耗大,建议避免跨库事务
⭐️全局唯一 ID:使用雪花算法或美团 Leaf,避免自增 ID 冲突
⭐️跨分片查询:禁止不带分片键的查询(如 WHERE name = '张三')
⭐️数据迁移:使用双写+灰度方案,平滑迁移数据
⭐️容量规划:分片数量按未来 3-5 年数据量设计,避免频繁扩容
5.高可用架构选型决策树
bash
单实例 QPS < 5000 ?
├── 是:单实例 + 每日备份
│
├── 否:需要高可用
数据量 < 1TB ?
├── 是:主从复制 + 读写分离
故障恢复时间要求 < 30秒 ?
├── 是:MySQL InnoDB Cluster (MGR)
└── 否:MHA + Keepalived
│
└── 否:必须分片
业务是否可拆分 ?
├── 是:垂直分库 + 独立主从
└── 否:水平分片 + ShardingSphere
6.总结
MySQL 高可用与扩展方案的核心在于 "分层解决" :
⭐️复制层:主从复制是基础,解决数据冗余和读写分离
⭐️集群层:InnoDB Cluster 解决自动故障转移
⭐️分片层:分库分表解决数据量和并发瓶颈
⭐️中间件层:ShardingSphere/ProxySQL 屏蔽底层复杂性
生产环境推荐组合:
⭐️中小型业务:主从复制 + ProxySQL + XtraBackup 备份
⭐️大型互联网:InnoDB Cluster + ShardingSphere + Redis 缓存
⭐️金融级:两地三中心部署 + 半同步复制 + MHA 自动切换 + 审计插件
根据业务规模、团队能力和预算选择合适的方案,避免过度设计