一、分库分表:能不分就不分
1.1 为什么需要分库分表?
随着互联网应用规模扩大,单机MySQL面临三大瓶颈:
-
数据量过大:单表超500W行或2GB数据时性能显著下降
-
并发访问过高:频繁的读写操作导致I/O和锁竞争
-
单点故障风险:服务宕机将导致业务完全不可用
传统优化手段(索引优化、SQL调优、硬件升级)在数据量达到亿级别时收效甚微,必须引入分布式架构。
1.2 分库分表的优势
| 性能提升 | 将负载分散到多个节点,提高并发处理能力 |
| 高可用性 | 数据多副本存储,单节点故障不影响服务 |
| 水平扩展 | 可随数据增长动态增加节点,避免垂直扩展瓶颈 |
| 成本优化 | 可使用廉价硬件构建大规模集群 |
1.3 分库分表的挑战
分库分表绝非简单的数据拆分,需解决以下分布式难题:
-
主键避重:分布式环境下的全局唯一ID生成
-
数据一致性:跨节点事务与数据同步
-
SQL路由:如何将查询路由到正确的分片
-
跨节点查询:分页、排序、聚合等操作的合并
-
数据迁移:扩容时的数据重分布
阿里开发手册建议:预估三年内单表数据超500W或单表大小超2GB时,需考虑分库分表。
二、MySQL集群搭建实战
2.1 基础MySQL服务安装
以CentOS 7 + MySQL 8.0.20为例:
bash
# 创建mysql用户组和用户
groupadd mysql
useradd -r -g mysql -s /bin/false mysql
# 解压并安装
tar -zxvf mysql-8.0.20-el7-x86_64.tar.gz
ln -s mysql-8.0.20-el7-x86_64 mysql
cd mysql
mkdir mysql-files
chown mysql:mysql mysql-files
chmod 750 mysql-files
# 初始化数据文件(注意保存生成的root临时密码)
bin/mysqld –initialize –user=mysql
bin/mysql_ssl_rsa_setup
# 启动服务
bin/mysqld_safe –user=mysql &
2.2 主从同步集群搭建
2.2.1 主节点配置(my.cnf)
ini
[mysqld]
server-id = 47
log_bin = master-bin
log_bin_index = master-bin.index
skip-name-resolve
2.2.2 从节点配置(my.cnf)
ini
[mysqld]
server-id = 48
relay_log = slave-relay-bin
relay_log_index = slave-relay-bin.index
log_bin = mysql-bin
log_slave_updates = 1
2.2.3 配置同步关系
sql
— 在主节点创建同步用户
GRANT REPLICATION SLAVE ON *.* TO 'repl'@'%' IDENTIFIED BY 'password';
— 查看主节点状态(记录File和Position)
SHOW MASTER STATUS;
— 在从节点配置主从关系
CHANGE MASTER TO
MASTER_HOST='192.168.232.128',
MASTER_PORT=3306,
MASTER_USER='repl',
MASTER_PASSWORD='password',
MASTER_LOG_FILE='master-bin.000001',
MASTER_LOG_POS=156;
— 启动从节点同步
START SLAVE;
SHOW SLAVE STATUS\\G;
关键验证点:
-
Slave_IO_Running: Yes
-
Slave_SQL_Running: Yes
-
Master_Log_File和Read_Master_Log_Pos与主节点一致
2.3 GTID同步模式
基于全局事务ID的同步方式,避免传统binlog位置同步的复杂度:
主节点配置:
ini
gtid_mode = on
enforce_gtid_consistency = on
log_bin = on
server_id = 47
binlog_format = row
从节点配置:
ini
gtid_mode = on
enforce_gtid_consistency = on
log_slave_updates = on
server_id = 48
2.4 半同步复制
解决异步复制可能丢失数据的问题:
sql
— 主节点安装半同步插件
INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
SET GLOBAL rpl_semi_sync_master_enabled = ON;
— 从节点安装半同步插件
INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
SET GLOBAL rpl_semi_sync_slave_enabled = ON;
STOP SLAVE;
START SLAVE;
半同步工作机制:
-
主节点提交事务后,等待至少一个从节点确认收到binlog
-
默认等待超时10秒,超时后降级为异步复制
-
提供AFTER_SYNC(默认)和AFTER_COMMIT两种模式
2.5 集群扩容与数据迁移
bash
# 使用mysqldump导出全量数据
mysqldump -u root -p –all-databases –single-transaction > full_backup.sql
# 在新节点导入数据
mysql -u root -p < full_backup.sql
# 配置新节点为从节点
2.6 MySQL高可用方案对比
| MMM | VIP漂移,主备切换 | 配置简单,读写高可用 | 易丢数据,社区维护少 | 基于日志点复制,读写高可用 |
| MHA | 故障自动切换,数据补偿 | 支持GTID,数据丢失少 | 需单独部署Manager节点 | 对数据一致性要求较高 |
| MGR | 基于Paxos协议的组复制 | 强一致性,自动选主 | 仅支持InnoDB,限制较多 | 金融级强一致场景 |
MGR集群架构示意图:
text
[Client] → [Primary Node] → [Secondary Nodes]
写请求 | |
同步复制 同步复制
v v
[数据一致性协议] → [多数节点确认]
三、应用层多数据源管理
3.1 Spring多数据源配置
java
@Configuration
public class DataSourceConfig {
@Bean(name = "masterDataSource")
@ConfigurationProperties(prefix = "spring.datasource.master")
public DataSource masterDataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "slaveDataSource")
@ConfigurationProperties(prefix = "spring.datasource.slave")
public DataSource slaveDataSource() {
return DataSourceBuilder.create().build();
}
@Primary
@Bean(name = "dynamicDataSource")
public DataSource dynamicDataSource() {
Map<Object, Object> targetDataSources = new HashMap<>();
targetDataSources.put(DataSourceType.MASTER, masterDataSource());
targetDataSources.put(DataSourceType.SLAVE, slaveDataSource());
DynamicDataSource dataSource = new DynamicDataSource();
dataSource.setTargetDataSources(targetDataSources);
dataSource.setDefaultTargetDataSource(masterDataSource());
return dataSource;
}
}
3.2 动态数据源切换
java
public class DataSourceContextHolder {
private static final ThreadLocal<String> CONTEXT_HOLDER = new ThreadLocal<>();
public static void setDataSource(String dataSource) {
CONTEXT_HOLDER.set(dataSource);
}
public static String getDataSource() {
return CONTEXT_HOLDER.get();
}
public static void clearDataSource() {
CONTEXT_HOLDER.remove();
}
}
@Aspect
@Component
public class DataSourceAspect {
@Before("@annotation(readOnly)")
public void setReadDataSource(JoinPoint point, ReadOnly readOnly) {
// 根据方法注解切换到从库
DataSourceContextHolder.setDataSource(DataSourceType.SLAVE);
}
@Before("execution(* com..*.insert*(..)) || " +
"execution(* com..*.update*(..)) || " +
"execution(* com..*.delete*(..))")
public void setWriteDataSource(JoinPoint point) {
// 写操作切换到主库
DataSourceContextHolder.setDataSource(DataSourceType.MASTER);
}
}
3.3 读写分离实现原理
text
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Client │───▶│ Application │───▶│ DataSource │
└─────────────┘ │ Layer │ │ Router │
└─────────────┘ └──────┬──────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Master │ │ Slave1 │ │ SlaveN │
│ Database │ │ Database │ │ Database │
└────────────┘ └────────────┘ └────────────┘
四、多数据源抽象与统一访问
4.1 使用DynamicDataSource框架
yaml
# application.yml 配置
dynamic:
datasource:
primary: master
strict: false
datasource:
master:
url: jdbc:mysql://master:3306/db
username: root
password: 123456
slave1:
url: jdbc:mysql://slave1:3306/db
username: root
password: 123456
slave2:
url: jdbc:mysql://slave2:3306/db
username: root
password: 123456
4.2 业务层透明访问
java
@Service
public class UserService {
// 无需关心数据源切换,框架自动路由
@Autowired
private UserMapper userMapper;
@ReadOnly // 读操作自动路由到从库
public User getUserById(Long id) {
return userMapper.selectById(id);
}
public void saveUser(User user) {
// 写操作自动路由到主库
userMapper.insert(user);
}
}
4.3 负载均衡策略
支持多种从库负载均衡算法:
-
轮询(Round Robin)
-
随机(Random)
-
权重(Weight)
-
最小连接数(Least Connections)
java
@Bean
public DataSource loadBalancedDataSource() {
Map<String, DataSource> dataSourceMap = new HashMap<>();
dataSourceMap.put("master", masterDataSource());
dataSourceMap.put("slave1", slave1DataSource());
dataSourceMap.put("slave2", slave2DataSource());
// 配置负载均衡规则
LoadBalanceRule rule = new RoundRobinRule();
return new LoadBalancedDataSource(dataSourceMap, rule);
}
五、总结与最佳实践
5.1 分库分表演进路线
第一阶段:单机MySQL + 读写分离
第二阶段:垂直分库(按业务拆分)
第三阶段:水平分表(按数据维度拆分)
第四阶段:引入分布式中间件(如ShardingSphere)
5.2 关键注意事项
数据一致性优先:宁可性能损失,也要保证数据正确性
渐进式拆分:先读写分离,再垂直拆分,最后水平拆分
监控完备:集群中每个节点都要有完善的监控告警
回滚方案:设计可逆的拆分方案,避免不可回退
5.3 性能调优建议
-
主从延迟控制在毫秒级
-
批量操作使用连接池,避免短连接
-
合理设置事务隔离级别
-
定期进行慢查询分析和索引优化
5.4 未来展望
随着云原生和Serverless架构的普及,数据库架构也在不断演进:
-
云数据库托管服务:阿里云RDS、AWS Aurora
-
NewSQL数据库:TiDB、CockroachDB
-
Serverless数据库:按需扩展,无需手动分片
本文详细介绍了从单机MySQL到分布式集群的完整演进路径,涵盖架构设计、部署实施、应用集成等关键环节。在实际项目中,建议根据业务特点和数据规模选择合适的方案,并始终将数据安全性和一致性放在首位。分库分表是一条不归路,决定前请三思,但一旦决定,就要设计好完整的迁移和容灾方案。






