Skip to content

Sharding-Sphere

Apache ShardingSphere 是一套开源的分布式数据库中间件解决方案,提供了数据分片、读写分离、分布式事务、数据加密等功能。它支持多种数据库(MySQL、PostgreSQL、SQL Server、Oracle 等),可以透明化地实现数据库的横向扩展。

核心特性

  • 数据分片:支持分库分表,支持多种分片策略
  • 读写分离:支持一主多从的读写分离
  • 分布式事务:支持 XA、Seata、BASE 等分布式事务
  • 数据加密:支持数据加密存储和查询
  • 分布式治理:支持配置中心、注册中心
  • 多数据库支持:支持 MySQL、PostgreSQL、Oracle 等

核心概念

  • 逻辑表:水平拆分的数据库(表)的同一类表的总称
  • 真实表:在分片的数据库中真实存在的物理表
  • 数据节点:数据分片的最小单元,由数据源名称和数据表组成
  • 分片键:用于分片的数据库字段
  • 分片算法:用于分片的算法

Spring Boot 集成

添加依赖

pom.xml 中添加 ShardingSphere 依赖:

xml
<dependency>
    <groupId>org.apache.shardingsphere</groupId>
    <artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
    <version>5.4.0</version>
</dependency>

分库分表配置

application.yml 中配置分库分表:

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: root
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db1
        username: root
        password: root
    rules:
      sharding:
        tables:
          # 配置逻辑表
          t_order:
            # 真实数据节点
            actual-data-nodes: ds$->{0..1}.t_order_$->{0..1}
            # 分库策略
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: database-inline
            # 分表策略
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: table-inline
        # 分片算法配置
        sharding-algorithms:
          database-inline:
            type: INLINE
            props:
              algorithm-expression: ds$->{user_id % 2}
          table-inline:
            type: INLINE
            props:
              algorithm-expression: t_order_$->{order_id % 2}
    # 显示 SQL
    props:
      sql-show: true

分片策略

标准分片策略

使用单个分片键进行分片:

yaml
table-strategy:
  standard:
    sharding-column: order_id
    sharding-algorithm-name: table-inline

复合分片策略

使用多个分片键进行分片:

yaml
table-strategy:
  complex:
    sharding-columns: user_id,order_id
    sharding-algorithm-name: complex-inline

行表达式分片策略

使用 Groovy 表达式进行分片:

yaml
sharding-algorithms:
  table-inline:
    type: INLINE
    props:
      algorithm-expression: t_order_$->{order_id % 4}

自定义分片策略

实现 StandardShardingAlgorithm 接口:

java
public class CustomShardingAlgorithm implements StandardShardingAlgorithm<Long> {
    
    @Override
    public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
        Long orderId = shardingValue.getValue();
        // 自定义分片逻辑
        String tableName = "t_order_" + (orderId % 4);
        return tableName;
    }
    
    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
        // 范围查询的分片逻辑
        return availableTargetNames;
    }
    
    @Override
    public void init() {
        // 初始化
    }
    
    @Override
    public String getType() {
        return "CUSTOM";
    }
}

读写分离

配置读写分离

yaml
spring:
  shardingsphere:
    datasource:
      names: master,slave0,slave1
      master:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/master
        username: root
        password: root
      slave0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/slave0
        username: root
        password: root
      slave1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/slave1
        username: root
        password: root
    rules:
      readwrite-splitting:
        data-sources:
          readwrite-ds:
            type: Static
            props:
              write-data-source-name: master
              read-data-source-names: slave0,slave1
              load-balancer-name: round-robin
        load-balancers:
          round-robin:
            type: ROUND_ROBIN

强制路由到主库

在某些场景下,需要强制从主库读取数据:

java
@Autowired
private HintManager hintManager;

public void forceMaster() {
    hintManager.setWriteRouteOnly();
    // 执行查询,会路由到主库
    // ...
    hintManager.close();
}

分布式事务

XA 事务

使用 XA 协议实现分布式事务:

yaml
spring:
  shardingsphere:
    rules:
      transaction:
        default-type: XA
        provider-type: Atomikos

Seata 事务

集成 Seata 实现分布式事务:

yaml
spring:
  shardingsphere:
    rules:
      transaction:
        default-type: BASE
        provider-type: Seata

在代码中使用:

java
@Transactional
public void createOrder(Order order) {
    // 跨库操作会自动使用分布式事务
    orderMapper.insert(order);
    orderItemMapper.insert(orderItem);
}

数据加密

配置数据加密

yaml
spring:
  shardingsphere:
    rules:
      encrypt:
        encryptors:
          aes-encryptor:
            type: AES
            props:
              aes-key-value: 123456
        tables:
          t_user:
            columns:
              phone:
                cipher-column: phone_cipher
                encryptor-name: aes-encryptor

使用加密字段

java
// 插入时自动加密
userMapper.insert(user); // phone 字段会自动加密存储到 phone_cipher

// 查询时自动解密
User user = userMapper.selectById(1); // phone_cipher 会自动解密到 phone

绑定表

绑定表是指分片规则一致的主表和子表,可以避免跨库关联查询:

yaml
spring:
  shardingsphere:
    rules:
      sharding:
        binding-tables:
          - t_order,t_order_item
        tables:
          t_order:
            actual-data-nodes: ds$->{0..1}.t_order_$->{0..1}
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: table-inline
          t_order_item:
            actual-data-nodes: ds$->{0..1}.t_order_item_$->{0..1}
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: table-inline

广播表

广播表是指所有分片数据源中都存在的表,表结构和数据在每个数据库中完全一致:

yaml
spring:
  shardingsphere:
    rules:
      sharding:
        broadcast-tables:
          - t_config

分片算法类型

取模分片

yaml
sharding-algorithms:
  mod-algorithm:
    type: MOD
    props:
      sharding-count: 4

哈希分片

yaml
sharding-algorithms:
  hash-algorithm:
    type: HASH_MOD
    props:
      sharding-count: 4

范围分片

yaml
sharding-algorithms:
  range-algorithm:
    type: CLASS_BASED
    props:
      strategy: STANDARD
      algorithmClassName: com.example.RangeShardingAlgorithm

时间分片

yaml
sharding-algorithms:
  time-algorithm:
    type: INTERVAL
    props:
      datetime-pattern: yyyy-MM-dd HH:mm:ss
      datetime-lower: 2020-01-01 00:00:00
      datetime-upper: 2024-12-31 23:59:59
      sharding-suffix-pattern: yyyyMM
      datetime-interval-amount: 1
      datetime-interval-unit: MONTHS

使用示例

实体类

java
@Data
@TableName("t_order")
public class Order {
    private Long orderId;
    private Long userId;
    private String orderNo;
    private BigDecimal amount;
    private Date createTime;
}

Mapper

java
@Mapper
public interface OrderMapper extends BaseMapper<Order> {
    
    @Select("SELECT * FROM t_order WHERE user_id = #{userId}")
    List<Order> selectByUserId(@Param("userId") Long userId);
}

Service

java
@Service
public class OrderService {
    
    @Autowired
    private OrderMapper orderMapper;
    
    public void createOrder(Order order) {
        orderMapper.insert(order);
    }
    
    public List<Order> getOrdersByUserId(Long userId) {
        return orderMapper.selectByUserId(userId);
    }
}

最佳实践

  1. 合理选择分片键:选择数据分布均匀的字段作为分片键
  2. 避免跨库事务:尽量在同一个分片内完成事务
  3. 使用绑定表:避免跨库关联查询
  4. 合理设置分片数量:避免分片过多导致管理复杂
  5. 监控和优化:监控 SQL 执行情况,优化慢查询
  6. 数据迁移:制定完善的数据迁移方案

常见问题

跨库查询

  • 使用绑定表避免跨库关联
  • 使用应用层聚合数据
  • 使用全局表(广播表)

分布式事务

  • 根据业务场景选择合适的分布式事务方案
  • XA 事务保证强一致性,但性能较低
  • BASE 事务保证最终一致性,性能较高

数据迁移

  • 使用 ShardingSphere 的数据迁移工具
  • 制定完善的迁移方案和回滚方案
  • 在业务低峰期进行迁移

ShardingSphere 作为强大的分库分表中间件,可以帮助企业轻松实现数据库的横向扩展,提升系统的性能和可扩展性。