ShardingJDBC 分库分表详细教程

ShardingJDBC 分库分表详细教程
ShardingJDBC 概述
Sharding-JDBC 是一款开源的 Java 生态下的分布式数据库解决方案,由当当网开源,现隶属于 Apache ShardingSphere 项目。它以 JDBC 驱动的形式提供,对应用透明,无需修改代码即可实现数据库的分库分表、读写分离、分布式事务等能力。
为什么需要分库分表?
单库单表瓶颈:
- 存储瓶颈:单表数据量超过 1000 万,查询性能下降
- 性能瓶颈:单表行数超过 2000 万,索引效率降低
- 并发瓶颈:单库连接数限制(通常 1000-2000 连接)
- 可用性瓶颈:单点故障影响整个业务
解决方案:
- 垂直拆分:按业务模块拆分不同数据库
- 水平拆分:按数据量拆分分库分表
Sharding-JDBC 核心特性
| 特性 | 说明 |
|---|---|
| 分库分表 | 支持按多种策略分片 |
| 读写分离 | 自动路由读/写请求 |
| 分布式事务 | 支持多种事务模式 |
| 数据库异构 | 支持 MySQL、Oracle、PostgreSQL 等 |
| 分片键路由 | 智能路由,减少跨库查询 |
| 数据库聚合 | 支持多表聚合查询 |
Sharding-JDBC 核心概念
1. 分片(Sharding)
分片是将数据按规则分散到多个物理数据库中。
分片维度:
- 分库:将数据分散到多个数据库实例
- 分表:在单个数据库中分散到多个表
分片键(Sharding Column):
- 用于计算分片规则的关键字段
- 如:user_id、order_id、tenant_id
- 分片键必须存在于查询条件中,否则会导致全路由
2. 数据节点
数据节点是分片的基本单元,由数据源 + 表组成。
“`yaml
数据节点:ds0.t_order_0
↓
数据源 表名
3. 分片策略
Sharding-JDBC 提供多种分片策略。
分片算法类型:
- 精确分片(Exact):使用 IN 关键字
- 范围分片(Range):使用 BETWEEN、>、< 等
- 复合分片(Complex):多分片键组合
分片算法模式:
- 标准分片(StandardShardingStrategy):单个分片键
- 复合分片(ComplexShardingStrategy):多个分片键
- 通用分片(InlineShardingStrategy):表达式分片
4. 数据源管理
yaml
dataSources:
ds0:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/sharding_db_0
username: root
password: root
ds1:
dataSourceClassName: com.zaxxer.hikari.HikariDataSource
driverClassName: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/sharding_db_1
username: root
password: root
Sharding-JDBC 配置示例
完整配置示例
yaml
application.yml
spring:
shardingsphere:
# 数据源配置
data-sources:
ds:
type: com.zaxxer.hikari.HikariDataSource
configuration:
driver: com.mysql.cj.jdbc.Driver
jdbcUrl: jdbc:mysql://localhost:3306/sharding_db
username: root
password: root
# 规则配置
rules:
sharding:
# 分片表配置
tables:
t_order:
actual-data-nodes: ds$->{0..1}.t_order$->{0..3}
# 分库策略
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: database-inline
# 分表策略
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: table-inline
# 主键策略(自动增 ID)
key-generate-strategy:
column: order_id
key-generator-name: snowflake
# 分片算法配置
sharding-algorithms:
database-inline:
type: INLINE
props:
algorithm-expression: ds$->{user_id % 2}
table-inline:
type: INLINE
props:
algorithm-expression: t_order$->{order_id % 4}
# 分布式主键生成器
key-generators:
snowflake:
type: SNOWFLAKE
props:
worker-id: 1
# 其他配置
props:
sql-show: true # 打印 SQL
exception-check-enabled: true
代码配置示例(Java)
java
@Configuration
public class ShardingConfig {
@Bean
public DataSource dataSource() throws SQLException {
// 配置数据源
Properties dsProps = new Properties();
dsProps.put(“user”, “root”);
dsProps.put(“password”, “root”);
dsProps.put(“jdbcUrl”, “jdbc:mysql://localhost:3306/sharding_db”);
dsProps.put(“driver”, “com.mysql.cj.jdbc.Driver”);
Map
dataSourceMap.put(“ds”, new HikariDataSource(dsProps));
// 配置分片规则
ShardingRuleConfig shardingRuleConfig = new ShardingRuleConfig();
// 分片算法
ShardingAlgorithmConfig databaseAlgorithm = new ShardingAlgorithmConfig();
databaseAlgorithm.setType(“INLINE”);
databaseAlgorithm.setProps(PropsValue.create(“algorithm-expression”, “ds$->{user_id % 2}”));
ShardingAlgorithmConfig tableAlgorithm = new ShardingAlgorithmConfig();
tableAlgorithm.setType(“INLINE”);
tableAlgorithm.setProps(PropsValue.create(“algorithm-expression”, “t_order$->{order_id % 4}”));
// 分片策略
ShardingColumn shardingColumn = new ShardingColumn();
shardingColumn.setShardingColumnName(“user_id”);
shardingRuleConfig.setDatabaseShardingStrategy(
new StandardShardingStrategyConfig(shardingColumn, databaseAlgorithm)
);
shardingColumn.setShardingColumnName(“order_id”);
shardingRuleConfig.setTableShardingStrategy(
new StandardShardingStrategyConfig(shardingColumn, tableAlgorithm)
);
// 表配置
ShardingTableConfig tableConfig = new ShardingTableConfig(“t_order”,
“ds$->{0..1}.t_order$->{0..3}”);
tableConfig.setDatabaseShardingStrategy(“database-inline”);
tableConfig.setTableShardingStrategy(“table-inline”);
tableConfig.setKeyGenerateStrategy(
new KeyGenerateStrategyConfig(“order_id”, “snowflake”)
);
shardingRuleConfig.getTables().add(tableConfig);
// 注册分片规则
ShardingRuleBuilder shardingRuleBuilder = new ShardingRuleBuilder();
shardingRuleBuilder.setDataSourceMap(dataSourceMap);
shardingRuleBuilder.setShardingRule(shardingRuleConfig);
return shardingRuleBuilder.build();
}
}
分片策略详解
策略一:取模分片
最常用的分片策略,根据分片键取模计算。
yaml
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: database-mod
sharding-algorithms:
database-mod:
type: MOD
props:
sharding-count: 2 # 2 个库
适用场景:数据均匀分布,查询条件包含分片键
优缺点:
- ✅ 数据分布均匀
- ✅ 实现简单
- ❌ 扩容需要数据迁移
策略二:范围分片
根据范围值分片,适用于时间范围。
yaml
database-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: database-range
sharding-algorithms:
database-range:
type: RANGE-HASH-MOD
props:
sharding-count: 4
mod-base: 24 # 时间粒度:小时
适用场景:按时间范围分片(日志、订单)
优缺点:
- ✅ 适合时间范围查询
- ❌ 可能导致数据倾斜
策略三:复合分片
使用多个分片键进行分片。
yaml
table-strategy:
complex:
sharding-columns: user_id,order_id
sharding-algorithm-name: table-complex
sharding-algorithms:
table-complex:
type: COMPLEX
props:
strategy: MOD
columns: user_id,order_id
适用场景:多条件分片,需要更细粒度控制
策略四:监听分片
结合多种分片策略,灵活配置。
yaml
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: table-hint
sharding-algorithms:
table-hint:
type: HINT
props:
sharding-algorithm-expression: t_order$->{order_id % 4}
特点:通过 Hint 手动指定分片,适合复杂查询
读写分离
配置示例
yaml
spring:
shardingsphere:
rules:
sharding:
data-sources:
ds:
readwrite-writing-data-source: ds-master
readwrite:
read-data-sources: ds-slave1,ds-slave2
readwrite:
name-rule: readwrite-splitting-rs
sharding-algorithm:
type: LOAD_BALANCE # 轮询算法
props:
write-data-source-name: ds-master
read-data-source-names: ds-slave1,ds-slave2
代码使用
java
// 写操作(自动路由到主库)
@PostMapping(“/order”)
public Order createOrder(@RequestBody Order order) {
return orderMapper.insert(order); // 写入主库
}
// 读操作(自动路由到从库)
@GetMapping(“/order/{id}”)
public Order getOrder(@PathVariable Long id) {
return orderMapper.selectById(id); // 读取从库
}
// 强制主库查询
@Query(value = “SELECT * FROM t_order WHERE order_id = ?1 FOR UPDATE”,
readOnly = false)
Optional
读写分离最佳实践
yaml
props:
readwrite-splitting-enabled: true
readwrite-splitting-query-with-catchable: true # 判断是否需要读主库
分布式事务
1. 本地模式(Local)
单数据库事务,性能最佳。
yaml
props:
transaction:
type: LOCAL
2. 最大努力交付模式(Max Effort)
最终一致性,适合非关键业务。
yaml
props:
transaction:
type: MAX
3. 事务模式(XA)
强一致性,性能较差。
yaml
props:
transaction:
type: XA
4. Seata 模式
集成 Seata 分布式事务框架。
yaml
props:
transaction:
type: SEATA
事务使用示例
java
@Transactional
public void createOrderWithInventory(Order order) {
// 1. 插入订单(不同库)
orderMapper.insert(order);
// 2. 扣减库存(同一库)
inventoryMapper.decrease(order.getProductId(), order.getQuantity());
// 3. 记录日志(同库)
logMapper.insert(order.getId(), “ORDER_CREATED”);
// 自动提交或回滚
}
常见场景与最佳实践
场景一:电商订单系统
分库分表策略:
yaml
分库:按用户 ID 取模
database-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: user-database
分表:按订单 ID 取模
table-strategy:
standard:
sharding-column: order_id
sharding-algorithm-name: order-table
实际数据节点:ds$->{0..3}.t_order$->{0..7}
优势:
- 用户相关订单集中在同一库
- 订单数据均匀分布
- 查询用户订单性能高
场景二:日志系统
分库分表策略:
yaml
按时间范围分表
table-strategy:
standard:
sharding-column: create_time
sharding-algorithm-name: time-table
实际数据节点:ds0.log$->{0..11} # 按月分表
优势:
- 按时间范围查询高效
- 历史数据可归档
场景三:多租户系统
分库分表策略:
yaml
按租户 ID 分库
database-strategy:
standard:
sharding-column: tenant_id
sharding-algorithm-name: tenant-database
表内分片
table-strategy:
standard:
sharding-column: user_id
sharding-algorithm-name: user-table
优势:
- 租户数据隔离
- 资源独立管理
最佳实践建议
1. 分片键选择
java
// ✅ 好的分片键
// – 查询条件中常用
// – 数据分布均匀
// – 值域范围大
// ❌ 避免的分片键
// – 查询条件中不常用
// – 数据分布不均
// – 值域范围小(导致分片不均)
2. 跨库查询处理
java
// 避免:全库扫描
// SELECT * FROM t_order WHERE create_time > ?
// 推荐:使用分片键
// SELECT * FROM t_order WHERE user_id = ? AND create_time > ?
// 或:使用广播表
// 配置常量表为广播表
3. 扩容策略
yaml
数据迁移方案
- 新库创建(ds_new)
- 双写(同时写旧库和新库)
- 数据同步(批量 + 增量)
- 切换流量(灰度切换)
- 停止旧库
- 查询响应时间
- 分片路由失败率
- 跨库查询比例
- 数据倾斜程度
- 订单量:日均 100 万,峰值 500 万
- 单表行数:超过 1 亿
- 查询延迟:P99 超过 2 秒
- 分库:4 个数据库(按 user_id 取模)
- 分表:每个库 8 张表(按 order_id 取模)
- 读写分离:1 主 2 从
- 主键生成:雪花算法 效果:
- 查询延迟:P99 降至 200ms
- 吞吐量:提升 10 倍
- 稳定性:99.99%
- 零侵入:无需修改业务代码
- 高性能:智能路由,减少跨库查询
- 易扩展:支持动态扩容
- 功能丰富:读写分离、分布式事务、数据库聚合
- 合理选择分片键
- 设计合适的分片策略
- 避免跨库查询
- 做好监控告警
- 制定扩容方案 通过合理应用 Sharding-JDBC,可以有效解决单库单表的性能瓶颈,支撑海量数据和高并发场景。 --- Maven 依赖:
4. 监控告警
java
// 关键指标监控
5. SQL 规范
sql
— ✅ 推荐
SELECT * FROM t_order WHERE user_id = ?;
SELECT * FROM t_order WHERE user_id IN (?, ?, ?);
— ❌ 避免
SELECT * FROM t_order; — 全路由
SELECT * FROM t_order WHERE order_time > ?; — 跨库
实际案例分析
案例:某电商平台订单系统
背景:
分片方案:
总结
Sharding-JDBC 作为成熟的分库分表解决方案,具有:
核心要点:
xml
“`



发表评论