1. 项目概述:为什么需要分库分表?
在互联网应用快速发展的今天,数据量呈现爆炸式增长。我经历过一个电商项目,仅仅一年时间订单表就突破了5000万条记录,单表查询性能明显下降。这就是典型的需要考虑分库分表的场景。
分库分表的核心思想是将一个庞大的数据库拆分成多个较小的、更易管理的部分。就像图书馆把书籍分类到不同的书架,可以显著提高检索效率。Sharding-JDBC作为轻量级的Java框架,完美实现了这一理念,它能在应用层透明地完成数据分片,而无需改动业务代码。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与基础配置
2.1 依赖引入
首先在Spring Boot项目中引入关键依赖:
xml复制<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>5.1.1</version>
</dependency>
注意:版本选择很重要,建议使用最新的稳定版以避免已知问题。
2.2 数据源配置
配置多个数据源是分库的基础。这里展示一个典型的两库配置:
yaml复制spring:
shardingsphere:
datasource:
names: ds0,ds1
ds0:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db0
username: root
password: 123456
ds1:
type: com.zaxxer.hikari.HikariDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://localhost:3306/db1
username: root
password: 123456
3. 分片策略详解
3.1 水平分表策略
假设我们有一个订单表orders,需要按照用户ID的哈希值分到4个表中:
yaml复制spring:
shardingsphere:
sharding:
tables:
orders:
actual-data-nodes: ds$->{0..1}.orders_$->{0..1}
table-strategy:
inline:
sharding-column: user_id
algorithm-expression: orders_$->{user_id % 4 / 2}
database-strategy:
inline:
sharding-column: user_id
algorithm-expression: ds$->{user_id % 2}
这个配置实现了:
- 按user_id的奇偶性分库
- 每库内再分2个表
3.2 范围分片策略
对于时间序列数据,范围分片更合适:
java复制public class DateRangeShardingAlgorithm implements PreciseShardingAlgorithm<Date> {
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Date> shardingValue) {
Date date = shardingValue.getValue();
Calendar calendar = Calendar.getInstance();
calendar.setTime(date);
int year = calendar.get(Calendar.YEAR);
return "orders_" + year;
}
}
4. 实战中的关键问题
4.1 分布式ID生成
分库分表后,自增ID不再适用。推荐使用Snowflake算法:
java复制public class SnowflakeIdGenerator {
private final long workerId;
private final long datacenterId;
private long sequence = 0L;
private long lastTimestamp = -1L;
// 实现细节省略...
}
4.2 跨库JOIN处理
Sharding-JDBC支持有限度的跨库JOIN,但性能较差。实际项目中我通常采用以下方案:
- 冗余关键字段
- 使用内存计算
- 考虑使用Elasticsearch等搜索引擎
5. 性能优化技巧
5.1 绑定表配置
对于经常关联查询的表,配置绑定关系避免笛卡尔积:
yaml复制spring:
shardingsphere:
sharding:
binding-tables:
- orders,order_detail
5.2 读写分离配置
结合分库分表与读写分离:
yaml复制spring:
shardingsphere:
masterslave:
name: ms_ds
master-data-source-name: ds_master
slave-data-source-names: ds_slave0,ds_slave1
load-balance-algorithm-type: round_robin
6. 监控与运维
6.1 SQL日志分析
开启SQL日志有助于排查问题:
yaml复制spring:
shardingsphere:
props:
sql.show: true
6.2 分布式事务管理
对于需要强一致性的场景,可以使用Seata集成:
java复制@GlobalTransactional
public void placeOrder(Order order) {
// 业务逻辑
}
7. 完整项目结构
一个典型的分库分表项目结构如下:
code复制src/main/java
├── config
│ ├── ShardingConfig.java
│ └── SnowflakeConfig.java
├── entity
│ └── Order.java
├── repository
│ └── OrderRepository.java
└── service
└── OrderService.java
8. 常见问题解决方案
8.1 分片键选择问题
经验法则:
- 选择高基数列
- 避免热点数据
- 考虑业务查询模式
8.2 数据迁移方案
推荐使用阿里云的DTS工具或自研迁移程序,关键步骤:
- 双写过渡期
- 数据校验
- 流量切换
9. 进阶话题
9.1 弹性伸缩实现
通过动态修改分片算法实现扩容:
java复制public class DynamicShardingAlgorithm implements StandardShardingAlgorithm<Long> {
private volatile int shardCount = 4;
public void setShardCount(int count) {
this.shardCount = count;
}
// 其他实现...
}
9.2 多租户方案
结合分库分表实现SaaS多租户:
yaml复制spring:
shardingsphere:
sharding:
default-database-strategy:
hint:
algorithm-class-name: com.example.TenantShardingAlgorithm
10. 最佳实践总结
经过多个项目的实践,我总结了以下关键点:
- 分片键选择比算法更重要
- 预留足够的扩展空间
- 监控必须先行
- 考虑冷热数据分离
- 测试环境要模拟真实数据量
在最近的一个金融项目中,我们通过合理的分库分表设计,将查询性能提升了8倍,同时保持了系统的可维护性。
