ShardingJDBC 分库分表详细教程

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 = new HashMap<>();
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 getByIdWithLock(Long id);


读写分离最佳实践

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

数据迁移方案

  1. 新库创建(ds_new)
  2. 双写(同时写旧库和新库)
  3. 数据同步(批量 + 增量)
  4. 切换流量(灰度切换)
  5. 停止旧库
  6. 
    

    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 > ?; — 跨库

    
    

    实际案例分析

    案例:某电商平台订单系统

    背景
    • 订单量:日均 100 万,峰值 500 万
    • 单表行数:超过 1 亿
    • 查询延迟:P99 超过 2 秒
    分片方案
    1. 分库:4 个数据库(按 user_id 取模)
    2. 分表:每个库 8 张表(按 order_id 取模)
    3. 读写分离:1 主 2 从
    4. 主键生成:雪花算法
    5. 效果
      • 查询延迟:P99 降至 200ms
      • 吞吐量:提升 10 倍
      • 稳定性:99.99%

      总结

      Sharding-JDBC 作为成熟的分库分表解决方案,具有:
      • 零侵入:无需修改业务代码
      • 高性能:智能路由,减少跨库查询
      • 易扩展:支持动态扩容
      • 功能丰富:读写分离、分布式事务、数据库聚合
      核心要点
      1. 合理选择分片键
      2. 设计合适的分片策略
      3. 避免跨库查询
      4. 做好监控告警
      5. 制定扩容方案
      6. 通过合理应用 Sharding-JDBC,可以有效解决单库单表的性能瓶颈,支撑海量数据和高并发场景。 --- Maven 依赖

    xml

    org.apache.shardingsphere
    shardingsphere-jdbc-core
    5.3.2

    “`

© 版权声明
THE END
喜欢就支持一下吧
点赞14 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容