找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1955

积分

0

好友

272

主题
发表于 7 天前 | 查看: 17| 回复: 0

ShardingSphere 作为一款强大的分布式数据库中间件,其数据分片功能的灵活配置是应对海量数据存储与访问的关键。本文将深入解析 ShardingSphere 数据分片策略的配置方法,涵盖从基础模式到高级策略的完整配置方案。

一、基础配置模式

1. Spring Boot YAML 配置(推荐)

通过 YAML 配置文件进行声明式配置,是 Spring Boot 项目中最常用且清晰的方式。

# application.yml
spring:
  shardingsphere:
    # ========== 1. 数据源配置 ==========
    datasource:
      names: ds0, ds1  # 数据源名称列表

    # 数据源0配置
      ds0:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db0?useSSL=false&serverTimezone=UTC
        username: root
        password: 123456
        # Hikari连接池配置
        maximum-pool-size: 20
        minimum-idle: 5
        connection-timeout: 30000
        idle-timeout: 600000
        max-lifetime: 1800000

    # 数据源1配置
      ds1:
        type: com.zaxxer.hikari.HikariDataSource
        driver-class-name: com.mysql.cj.jdbc.Driver
        jdbc-url: jdbc:mysql://localhost:3306/db1?useSSL=false&serverTimezone=UTC
        username: root
        password: 123456

    # ========== 2. 分片规则配置 ==========
    rules:
      - !SHARDING
        # 2.1 分片表配置
        tables:
          # 订单表分片配置
          t_order:
            # 实际数据节点:ds0.t_order_0, ds0.t_order_1, ds1.t_order_0, ds1.t_order_1
            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     # 分片算法

            # 分布式主键生成策略
            key-generate-strategy:
              column: order_id                # 主键列
              key-generator-name: snowflake   # 主键生成器

          # 订单明细表分片配置
          t_order_item:
            actual-data-nodes: ds${0..1}.t_order_item_${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

        # 2.2 绑定表配置(优化关联查询)
        binding-tables:
          - t_order,t_order_item  # 订单表和订单明细表绑定

        # 2.3 广播表配置(每个库都有全量数据)
        broadcast-tables:
          - t_config              # 配置表
          - t_city                # 城市表

        # 2.4 默认分库策略(未配置分片策略的表使用)
        default-database-strategy:
          none:  # 不分库

        # 2.5 默认分表策略
        default-table-strategy:
          none:  # 不分表

        # 2.6 分片算法定义
        sharding-algorithms:
          # 行表达式分片算法(按user_id分库)
          database_inline:
            type: INLINE
            props:
              algorithm-expression: ds${user_id % 2} # 取模分片

          # 行表达式分片算法(按order_id分表)
          table_inline:
            type: INLINE
            props:
              algorithm-expression: t_order_${order_id % 2}

          # 时间范围分片算法(按月分表)
          table_interval:
            type: INTERVAL
            props:
              datetime-pattern: "yyyy-MM-dd HH:mm:ss"
              datetime-lower: "2023-01-01 00:00:00"
              datetime-upper: "2025-12-31 23:59:59"
              sharding-suffix-pattern: "yyyyMM"
              datetime-interval-amount: 1
              datetime-interval-unit: "MONTHS"

          # 哈希取模分片算法
          hash_mod:
            type: HASH_MOD
            props:
              sharding-count: 4  # 分片数量

        # 2.7 分布式序列算法
        key-generators:
          snowflake:
            type: SNOWFLAKE
            props:
              worker-id: 123      # 工作机器ID
              max-vibration-offset: 3  # 最大抖动偏移量

    # ========== 3. 其他功能配置 ==========
    props:
      # 是否显示SQL(调试用)
      sql-show: true
      # SQL是否简化显示
      sql-simple: false
      # 查询结果集是否并行执行
      executor-size: 10
      # 最大连接数限制
      max-connections-size-per-query: 1
      # 是否在启动时检查分片元数据
      check-table-metadata-enabled: false
      # 是否开启XA事务
      xa-transaction-manager-type: Atomikos

2. Java 代码配置(灵活配置)

对于需要动态生成规则或进行复杂逻辑控制的场景,编程式配置提供了更高的灵活性。

@Configuration
public class ShardingConfig {

    /**
     * 编程式配置分片规则
     */
    @Bean
    public DataSource dataSource() throws SQLException {
        // 1. 配置数据源映射
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put("ds0", createDataSource("jdbc:mysql://localhost:3306/db0"));
        dataSourceMap.put("ds1", createDataSource("jdbc:mysql://localhost:3306/db1"));

        // 2. 配置分片规则
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();

        // 2.1 订单表分片规则
        TableRuleConfiguration orderTableRuleConfig = new TableRuleConfiguration(
            "t_order",
            "ds${0..1}.t_order_${0..1}" // 实际数据节点
        );

        // 分库策略:按user_id取模
        orderTableRuleConfig.setDatabaseShardingStrategy(
            new StandardShardingStrategyConfiguration(
                "user_id",
                new PreciseShardingAlgorithm<Long>() {
                    @Override
                    public String doSharding(Collection<String> availableTargetNames,
                                             PreciseShardingValue<Long> shardingValue) {
                        long userId = shardingValue.getValue();
                        for (String dsName : availableTargetNames) {
                            if (dsName.endsWith(userId % 2 + "")) {
                                return dsName;
                            }
                        }
                        throw new IllegalArgumentException();
                    }
                }
            )
        );

        // 分表策略:按order_id取模
        orderTableRuleConfig.setTableShardingStrategy(
            new StandardShardingStrategyConfiguration(
                "order_id",
                new PreciseShardingAlgorithm<Long>() {
                    @Override
                    public String doSharding(Collection<String> availableTargetNames,
                                             PreciseShardingValue<Long> shardingValue) {
                        long orderId = shardingValue.getValue();
                        for (String tableName : availableTargetNames) {
                            if (tableName.endsWith(orderId % 2 + "")) {
                                return tableName;
                            }
                        }
                        throw new IllegalArgumentException();
                    }
                }
            )
        );

        // 主键生成策略
        orderTableRuleConfig.setKeyGenerateStrategy(
            new KeyGenerateStrategyConfiguration("order_id", "snowflake"));

        shardingRuleConfig.getTableRuleConfigs().add(orderTableRuleConfig);

        // 2.2 绑定表配置
        shardingRuleConfig.getBindingTableGroups().add("t_order,t_order_item");

        // 2.3 广播表配置
        shardingRuleConfig.getBroadcastTables().add("t_config");

        // 2.4 配置分布式序列算法
        Properties snowflakeProps = new Properties();
        snowflakeProps.setProperty("worker-id", "123");
        shardingRuleConfig.getKeyGenerators().put("snowflake",
            new KeyGenerateAlgorithmConfiguration("SNOWFLAKE", snowflakeProps));

        // 3. 创建ShardingSphere数据源
        return ShardingSphereDataSourceFactory.createDataSource(
            dataSourceMap,
            Collections.singleton(shardingRuleConfig),
            new Properties()
        );
    }

    private DataSource createDataSource(String url) {
        HikariDataSource dataSource = new HikariDataSource();
        dataSource.setJdbcUrl(url);
        dataSource.setUsername("root");
        dataSource.setPassword("123456");
        dataSource.setMaximumPoolSize(20);
        return dataSource;
    }
}

二、高级分片策略配置

1. 复合分片策略

当单一分片键无法满足业务需求时,可以使用多个分片键进行复合分片。

# 使用多个分片键进行分片
spring:
  shardingsphere:
    rules:
      - !SHARDING
        tables:
          t_order:
            actual-data-nodes: ds${0..3}.t_order_${0..15}

            # 复合分片策略:user_id + order_date
            database-strategy:
              complex:
                sharding-columns: user_id,order_date
                sharding-algorithm-name: database_complex

            table-strategy:
              complex:
                sharding-columns: order_id,create_time
                sharding-algorithm-name: table_complex

        sharding-algorithms:
          # 复合分片算法实现
          database_complex:
            type: CLASS_BASED
            props:
              strategy: COMPLEX
              algorithmClassName: com.example.sharding.ComplexDatabaseShardingAlgorithm

          table_complex:
            type: CLASS_BASED
            props:
              strategy: COMPLEX
              algorithmClassName: com.example.sharding.ComplexTableShardingAlgorithm
// 自定义复合分片算法
public class ComplexDatabaseShardingAlgorithm implements ComplexKeysShardingAlgorithm<Comparable<?>> {

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames,
                                         ComplexKeysShardingValue<Comparable<?>> shardingValue) {
        // 获取分片键值
        Map<String, Collection<Comparable<?>>> columnValues = shardingValue.getColumnNameAndShardingValuesMap();

        // user_id 分片
        Collection<Comparable<?>> userIds = columnValues.get("user_id");
        // order_date 分片
        Collection<Comparable<?>> orderDates = columnValues.get("order_date");

        // 自定义分片逻辑
        Set<String> result = new HashSet<>();

        for (Comparable<?> userId : userIds) {
            for (Comparable<?> orderDate : orderDates) {
                int dbIndex = calculateDatabaseIndex(userId, orderDate);
                String dbName = "ds" + dbIndex;
                if (availableTargetNames.contains(dbName)) {
                    result.add(dbName);
                }
            }
        }

        return result;
    }

    private int calculateDatabaseIndex(Object userId, Object orderDate) {
        // 示例:基于user_id和月份组合分片
        Long uid = Long.parseLong(userId.toString());
        LocalDate date = LocalDate.parse(orderDate.toString());

        int yearMonth = date.getYear() * 12 + date.getMonthValue();
        return (uid.hashCode() + yearMonth) % 4;  // 4个分库
    }
}

2. Hint 分片策略(强制路由)

Hint 策略不依赖于 SQL 中的分片键,允许通过编程方式强制指定数据路由的目标,适用于特殊场景如数据修补、多租户路由等。

spring:
  shardingsphere:
    rules:
      - !SHARDING
        tables:
          t_order:
            actual-data-nodes: ds${0..1}.t_order_${0..15}

            # Hint分片策略(不依赖分片键,通过代码指定路由)
            database-strategy:
              hint:
                sharding-algorithm-name: database_hint

            table-strategy:
              hint:
                sharding-algorithm-name: table_hint

        sharding-algorithms:
          database_hint:
            type: HINT_INLINE
            props:
              algorithm-expression: ds${Integer.parseInt(value) % 2}

          table_hint:
            type: HINT_INLINE
            props:
              algorithm-expression: t_order_${Integer.parseInt(value) % 16}
// 使用Hint强制路由
@Service
public class OrderService {

    public Order getOrderByHint(Long orderId, int shardValue) {
        try (HintManager hintManager = HintManager.getInstance()) {
            // 强制路由到指定分片
            hintManager.addDatabaseShardingValue("t_order", shardValue);
            hintManager.addTableShardingValue("t_order", shardValue);

            // 执行查询
            return orderMapper.selectById(orderId);
        }
    }

    // 读写分离场景:强制走主库
    public Order getOrderFromMaster(Long orderId) {
        try (HintManager hintManager = HintManager.getInstance()) {
            hintManager.setWriteRouteOnly();  // 强制路由到写库
            return orderMapper.selectById(orderId);
        }
    }
}

3. 范围分片策略(适合时间序列)

对于日志、流水等按时间增长的数据,范围分片策略(如按月、按年)是常见选择。

spring:
  shardingsphere:
    rules:
      - !SHARDING
        tables:
          t_log:
            # 按月分表:t_log_202301, t_log_202302, ...
            actual-data-nodes: ds0.t_log_${2023..2025}${[1,2,3,4,5,6,7,8,9,10,11,12]}

            table-strategy:
              standard:
                sharding-column: create_time
                sharding-algorithm-name: log_table_interval

        sharding-algorithms:
          # 时间范围分片算法
          log_table_interval:
            type: INTERVAL
            props:
              datetime-pattern: "yyyy-MM-dd HH:mm:ss"  # 时间格式
              datetime-lower: "2023-01-01 00:00:00"    # 下限
              datetime-upper: "2025-12-31 23:59:59"    # 上限
              sharding-suffix-pattern: "yyyyMM"        # 分表后缀格式
              datetime-interval-amount: 1              # 间隔数量
              datetime-interval-unit: "MONTHS"         # 间隔单位
              # datetime-interval-unit: "DAYS", "WEEKS", "MONTHS", "YEARS"

4. 自动分片策略(Auto Table)

对于不想手动管理大量分表名的情况,可以使用自动分片策略,ShardingSphere 会自动根据算法管理物理表。

spring:
  shardingsphere:
    rules:
      - !SHARDING
        # 自动分表(ShardingSphere自动管理分片)
        auto-tables:
          t_order_auto:
            actual-data-sources: ds0, ds1
            sharding-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: auto_mod

        sharding-algorithms:
          # 自动分片算法
          auto_mod:
            type: AUTO_MOD
            props:
              sharding-count: 4  # 自动创建4个分片

三、动态配置与治理中心

生产环境中,配置的动态更新和集中管理至关重要。ShardingSphere 支持集成 ZooKeeper、Nacos 等作为治理中心。

1. 基于ZooKeeper的动态配置

spring:
  shardingsphere:
    # 1. 运行模式配置
    mode:
      type: Cluster  # 集群模式
      repository:
        type: ZooKeeper  # 使用ZooKeeper存储配置
        props:
          namespace: sharding-sphere-demo
          server-lists: localhost:2181,localhost:2182,localhost:2183
          retryIntervalMilliseconds: 500
          timeToLiveSeconds: 60
          maxRetries: 3
          operationTimeoutMilliseconds: 500

    # 2. 配置从治理中心加载
    datasources: {}  # 留空,从ZooKeeper加载

    rules: []  # 留空,从ZooKeeper加载

    # 3. 属性配置
    props:
      sql-show: true
// 动态配置管理
@Service
public class DynamicConfigManager {

    @Autowired
    private GovernanceFacade governanceFacade;

    /**
     * 动态添加分片规则
     */
    public void addShardingRule(String logicTable, ShardingRuleConfiguration rule) {
        // 获取当前规则
        RuleConfiguration currentRule = governanceFacade.getRuleConfiguration();

        // 添加新规则
        if (currentRule instanceof ShardingRuleConfiguration) {
            ShardingRuleConfiguration shardingRule = (ShardingRuleConfiguration) currentRule;

            // 添加新的表规则
            TableRuleConfiguration tableRuleConfig = createTableRule(logicTable);
            shardingRule.getTableRuleConfigs().add(tableRuleConfig);

            // 更新到治理中心
            governanceFacade.persistRuleConfiguration(shardingRule);
        }
    }

    /**
     * 动态扩容:增加分片
     */
    public void addNewDataSource(String dsName, DataSource dataSource) {
        // 1. 添加新数据源
        governanceFacade.persistDataSourceConfiguration(dsName, dataSource);

        // 2. 更新分片规则
        updateShardingRuleForNewDataSource(dsName);

        // 3. 触发数据迁移
        triggerDataMigration(dsName);
    }
}

2. 基于Nacos的动态配置

spring:
  shardingsphere:
    mode:
      type: Cluster
      repository:
        type: NACOS
        props:
          server-lists: localhost:8848
          namespace: sharding-demo
          group: SHARDING_GROUP
          dataId: sharding-config
          username: nacos
          password: nacos
          timeout: 30000

四、多场景配置示例

场景1:电商订单系统(用户维度分片)

核心思想是按用户ID散列,将同一用户的数据尽量集中,并优化关联查询。

# 电商订单系统分片配置
spring:
  shardingsphere:
    datasource:
      names: ds0, ds1, ds2, ds3
      ds0: ...
      ds1: ...
      ds2: ...
      ds3: ...

    rules:
      - !SHARDING
        tables:
          # 订单表:按user_id分库,按order_id分表
          t_order:
            actual-data-nodes: ds${0..3}.order_${0..15}
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: order_db_hash
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: order_table_hash

          # 订单商品表:与订单表绑定
          t_order_item:
            actual-data-nodes: ds${0..3}.order_item_${0..15}
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: order_db_hash
            table-strategy:
              standard:
                sharding-column: order_id
                sharding-algorithm-name: order_table_hash

          # 用户表:按user_id分库不分表
          t_user:
            actual-data-nodes: ds${0..3}.t_user
            database-strategy:
              standard:
                sharding-column: user_id
                sharding-algorithm-name: user_db_hash

        # 绑定订单相关表
        binding-tables:
          - t_order,t_order_item

        # 广播表:配置、类目等
        broadcast-tables:
          - t_category
          - t_config
          - t_province
          - t_city

        # 分片算法
        sharding-algorithms:
          # 订单分库算法:user_id取模
          order_db_hash:
            type: HASH_MOD
            props:
              sharding-count: 4

          # 订单分表算法:order_id取模
          order_table_hash:
            type: HASH_MOD
            props:
              sharding-count: 16

          # 用户分库算法
          user_db_hash:
            type: HASH_MOD
            props:
              sharding-count: 4

场景2:日志系统(时间维度分片)

按时间(如月份)和业务线进行复合分片,便于按时间和业务维度进行数据管理和归档。

# 日志系统分片配置(按月分表,按业务线分库)
spring:
  shardingsphere:
    datasource:
      names: ds_log_biz1, ds_log_biz2, ds_log_biz3
      ds_log_biz1: ...
      ds_log_biz2: ...
      ds_log_biz3: ...

    rules:
      - !SHARDING
        tables:
          t_app_log:
            actual-data-nodes: ds_log_${['biz1','biz2','biz3']}.app_log_${2023..2025}${[1,2,3,4,5,6,7,8,9,10,11,12]}

            # 复合分片:按业务线分库,按时间分表
            database-strategy:
              standard:
                sharding-column: biz_code
                sharding-algorithm-name: log_db_inline

            table-strategy:
              standard:
                sharding-column: create_time
                sharding-algorithm-name: log_table_interval

        sharding-algorithms:
          # 按业务线分库
          log_db_inline:
            type: INLINE
            props:
              algorithm-expression: ds_log_${biz_code}

          # 按月分表
          log_table_interval:
            type: INTERVAL
            props:
              datetime-pattern: "yyyy-MM-dd HH:mm:ss"
              datetime-lower: "2023-01-01 00:00:00"
              datetime-upper: "2025-12-31 23:59:59"
              sharding-suffix-pattern: "yyyyMM"
              datetime-interval-amount: 1
              datetime-interval-unit: "MONTHS"

场景3:多租户SaaS系统

使用 Hint 分片策略,根据当前登录租户的上下文,将其数据路由到专属的数据库实例,实现数据隔离。

# 多租户系统分片配置
spring:
  shardingsphere:
    datasource:
      names: ds_tenant_0, ds_tenant_1, ds_tenant_2, ds_shared
      ds_tenant_0: ...  # 租户0专属库
      ds_tenant_1: ...  # 租户1专属库
      ds_tenant_2: ...  # 租户2专属库
      ds_shared: ...    # 共享库

    rules:
      - !SHARDING
        tables:
          # 租户数据表:按tenant_id分库
          t_tenant_data:
            actual-data-nodes: ds_tenant_${0..2}.t_tenant_data

            # Hint分片策略:根据当前租户上下文路由
            database-strategy:
              hint:
                sharding-algorithm-name: tenant_hint

          # 共享数据表:所有租户共用
          t_shared_data:
            actual-data-nodes: ds_shared.t_shared_data

        sharding-algorithms:
          # 租户Hint分片算法
          tenant_hint:
            type: CLASS_BASED
            props:
              strategy: HINT
              algorithmClassName: com.example.saas.TenantShardingAlgorithm
// 租户分片算法实现
public class TenantShardingAlgorithm implements HintShardingAlgorithm<String> {

    @Override
    public Collection<String> doSharding(Collection<String> availableTargetNames,
                                         HintShardingValue<String> shardingValue) {
        // 从ThreadLocal获取当前租户ID
        String tenantId = TenantContext.getCurrentTenantId();

        if (tenantId == null) {
            throw new IllegalStateException("No tenant context found");
        }

        // 根据租户ID路由到对应的数据源
        String targetDataSource = "ds_tenant_" + tenantId;

        if (availableTargetNames.contains(targetDataSource)) {
            return Collections.singletonList(targetDataSource);
        }

        throw new IllegalArgumentException("No data source found for tenant: " + tenantId);
    }
}

五、配置优化与最佳实践

1. 性能优化配置

合理的参数配置能显著提升分片集群的性能和稳定性。

spring:
  shardingsphere:
    props:
      # ========== 执行引擎配置 ==========
      # 执行模式:内存限制模式(默认)、连接限制模式
      sql-federation-enabled: false # 是否启用联邦查询
      sql-comment-parse-enabled: true # 是否解析SQL注释

      # 连接模式下的参数
      max-connections-size-per-query: 1 # 每个查询最大连接数
      check-table-metadata-enabled: false # 是否检查表元数据

      # 执行器配置
      executor-size: 10 # 工作线程数,默认CPU核心数*2
      kernel-executor-size: 0 # 内核工作线程数,0表示自动计算

      # ========== 查询优化配置 ==========
      # 是否允许范围查询路由到所有分片
      allow-range-query-with-inline-sharding: false

      # ========== 元数据配置 ==========
      metadata-full: false # 是否加载所有表的元数据

      # ========== 事务配置 ==========
      xa-transaction-manager-type: Atomikos  # XA事务管理器类型
      xa-recovery-interval-seconds: 60 # XA恢复间隔

2. 监控与日志配置

spring:
  shardingsphere:
    props:
      # SQL日志配置
      sql-show: true # 显示SQL
      sql-simple: false # 是否简化显示

      # 日志输出配置
      log-table-enabled: false # 是否启用日志表
      log-datasource: '' # 日志数据源
      log-table: '' # 日志表名

      # 分布式链路追踪
      opentracing-enabled: false # 是否启用OpenTracing
      metrics-enabled: false # 是否启用Metrics

    # Spring Boot Actuator集成
    health:
      enabled: true # 启用健康检查
    metrics:
      enabled: true # 启用Metrics

3. 多环境配置策略

为开发、测试、生产环境准备不同的配置,是保障工程效率和安全性的重要环节。

# application-dev.yml(开发环境)
spring:
  shardingsphere:
    props:
      sql-show: true  # 显示SQL便于调试
      sql-simple: false
    datasource:
      names: ds0  # 开发环境单库即可
      ds0: ...

# application-test.yml(测试环境)
spring:
  shardingsphere:
    props:
      sql-show: true
      sql-simple: true
    datasource:
      names: ds0, ds1  # 测试环境2个库
      ds0: ...
      ds1: ...

# application-prod.yml(生产环境)
spring:
  shardingsphere:
    props:
      sql-show: false  # 生产环境关闭SQL显示
      sql-simple: false
    datasource:
      names: ds0, ds1, ds2, ds3  # 生产环境4个库
      ds0: ...
      ds1: ...
      ds2: ...
      ds3: ...
    mode:
      type: Cluster  # 生产环境使用集群模式
      repository:
        type: ZooKeeper
        props:
          server-lists: zk1:2181,zk2:2181,zk3:2181

4. 配置验证与测试

编写测试用例是验证分片配置是否正确的可靠方式。

@SpringBootTest
@TestPropertySource(locations = "classpath:application-test.yml")
public class ShardingConfigTest {

    @Autowired
    private DataSource dataSource;

    @Test
    public void testShardingConfig() throws SQLException {
        // 1. 验证数据源类型
        Assert.assertTrue(dataSource instanceof ShardingSphereDataSource);

        // 2. 获取配置信息
        ShardingSphereDataSource shardingDataSource = (ShardingSphereDataSource) dataSource;
        ShardingSphereDatabase database = shardingDataSource.getDatabase();

        // 3. 验证分片规则
        Collection<ShardingTableRuleConfiguration> tableRules =
            database.getRuleMetaData().getConfigurations(ShardingTableRuleConfiguration.class);

        Assert.assertFalse(tableRules.isEmpty());

        // 4. 测试SQL执行
        try (Connection conn = dataSource.getConnection();
             Statement stmt = conn.createStatement()) {

            // 插入测试数据
            String sql = "INSERT INTO t_order (order_id, user_id, amount) VALUES (10001, 1001, 99.99)";
            int rows = stmt.executeUpdate(sql);
            Assert.assertEquals(1, rows);

            // 查询测试数据
            ResultSet rs = stmt.executeQuery("SELECT * FROM t_order WHERE order_id = 10001");
            Assert.assertTrue(rs.next());
            Assert.assertEquals(1001L, rs.getLong("user_id"));
        }
    }

    @Test
    public void testHintSharding() {
        // 测试Hint分片
        try (HintManager hintManager = HintManager.getInstance()) {
            hintManager.addDatabaseShardingValue("t_order", 0);
            hintManager.addTableShardingValue("t_order", 0);

            // 执行查询,应该路由到ds0.t_order_0
            Order order = orderService.getOrder(10001L);
            Assert.assertNotNull(order);
        }
    }
}

六、常见问题解决方案

1. 配置问题诊断

在应用启动或运行阶段,对分片配置进行自动化诊断,能提前发现潜在问题。

@Component
public class ShardingConfigValidator {

    @Autowired
    private DataSource dataSource;

    @PostConstruct
    public void validateConfig() {
        try {
            ShardingSphereDataSource shardingDataSource = (ShardingSphereDataSource) dataSource;

            // 1. 检查数据源连接
            checkDataSourceConnections(shardingDataSource);

            // 2. 检查分片规则
            checkShardingRules(shardingDataSource);

            // 3. 检查广播表数据一致性
            checkBroadcastTables(shardingDataSource);

            // 4. 检查分片算法配置
            checkShardingAlgorithms(shardingDataSource);

            log.info("Sharding configuration validation passed");

        } catch (Exception e) {
            log.error("Sharding configuration validation failed", e);
            throw new IllegalStateException("Sharding config validation failed", e);
        }
    }

    private void checkDataSourceConnections(ShardingSphereDataSource dataSource) throws SQLException {
        Map<String, DataSource> dataSourceMap = dataSource.getDataSourceMap();

        for (Map.Entry<String, DataSource> entry : dataSourceMap.entrySet()) {
            String dsName = entry.getKey();
            DataSource ds = entry.getValue();

            try (Connection conn = ds.getConnection();
                 Statement stmt = conn.createStatement()) {

                // 执行简单查询检查连接
                ResultSet rs = stmt.executeQuery("SELECT 1");
                if (!rs.next()) {
                    throw new SQLException("Data source " + dsName + " connection test failed");
                }

                log.info("Data source {} connection test passed", dsName);
            }
        }
    }
}

2. 动态配置更新

提供 API 接口,允许运维人员在不停机的情况下动态调整分片配置。

@RestController
@RequestMapping("/sharding/config")
public class ShardingConfigController {

    @Autowired
    private GovernanceFacade governanceFacade;

    /**
     * 动态添加分片
     */
    @PostMapping("/add-shard")
    public ResponseEntity<?> addShard(@RequestBody AddShardRequest request) {
        try {
            // 1. 添加新数据源
            DataSourceConfiguration dsConfig = createDataSourceConfig(request);
            governanceFacade.persistDataSourceConfiguration(
                request.getShardName(), dsConfig);

            // 2. 更新分片规则
            updateShardingRule(request.getShardName());

            // 3. 触发数据迁移(异步)
            CompletableFuture.runAsync(() -> {
                migrateDataToNewShard(request.getShardName());
            });

            return ResponseEntity.ok("Shard added successfully");

        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("Failed to add shard: " + e.getMessage());
        }
    }

    /**
     * 动态修改分片算法
     */
    @PostMapping("/update-algorithm")
    public ResponseEntity<?> updateAlgorithm(@RequestBody UpdateAlgorithmRequest request) {
        try {
            // 获取当前规则
            RuleConfiguration ruleConfig = governanceFacade.getRuleConfiguration();

            if (ruleConfig instanceof ShardingRuleConfiguration) {
                ShardingRuleConfiguration shardingRule = (ShardingRuleConfiguration) ruleConfig;

                // 更新分片算法
                ShardingAlgorithmConfiguration newAlgorithm = createAlgorithmConfig(request);
                shardingRule.getShardingAlgorithms().put(request.getAlgorithmName(), newAlgorithm);

                // 持久化更新
                governanceFacade.persistRuleConfiguration(shardingRule);

                return ResponseEntity.ok("Algorithm updated successfully");
            }

            return ResponseEntity.badRequest().body("Not a sharding rule configuration");

        } catch (Exception e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body("Failed to update algorithm: " + e.getMessage());
        }
    }
}

七、面试回答策略

结构化回答模板

“ShardingSphere的数据分片策略配置主要围绕四个核心方面:数据源配置、分片规则定义、分片算法选择、高级特性配置

  1. 数据源配置:首先定义多个物理数据源,配置连接池参数。这是分片的基础。
  2. 分片规则定义
    • 分片表配置:通过 actual-data-nodes 指定实际数据节点
    • 分片策略:分为分库策略和分表策略,支持标准、复合、Hint等多种策略
    • 绑定表配置:优化关联查询,避免笛卡尔积
    • 广播表配置:配置需要在所有分片都存在的小表
  3. 分片算法选择
    • 行表达式算法:适合简单取模分片
    • 哈希取模算法:数据分布均匀
    • 时间范围算法:适合按时间分片
    • 自定义算法:满足复杂业务需求
  4. 高级特性配置
    • 分布式主键:配置Snowflake等算法
    • 读写分离:与分片规则结合使用
    • 数据加密:透明化字段加密
    • 动态配置:基于ZooKeeper/Nacos的动态管理

实战经验
在我们电商项目中,订单表按 user_id % 8 分库,按 order_id % 32 分表,采用绑定表优化订单查询。配置时特别注意了广播表如商品类目表的同步,并通过Hint机制支持热点订单的特殊处理。”

常见追问及应对

  1. 如何选择分片算法?

    • :根据业务特点:离散数据用哈希取模,时间序列用范围分片,复杂业务用自定义算法。还要考虑扩容时的数据迁移成本。
  2. 绑定表有什么作用?

    • :避免关联查询产生笛卡尔积。例如订单表和订单明细表绑定后,JOIN查询只会关联相同分片的数据,性能提升显著。
  3. 如何配置才能平滑扩容?

    • :使用一致性哈希算法减少数据迁移;预留分片容量;配置双写迁移方案;通过治理中心动态更新配置。
  4. 生产环境配置有哪些注意事项?

    • :关闭SQL日志显示;启用健康检查和监控;配置连接池参数;使用集群模式保证高可用;有完整的回滚方案。
  5. 如何验证分片配置是否正确?

    • :编写单元测试验证路由逻辑;使用 sql-show: true 检查实际执行的SQL;通过监控观察数据分布是否均匀。

总结

配置 ShardingSphere 的数据分片策略是一项系统工程,需要结合具体业务场景、数据量、增长模式和访问模式来综合设计。从基础的取模分片到高级的动态治理,从简单的 YAML 配置到复杂的编程式控制,ShardingSphere 提供了丰富的工具来应对各种挑战。掌握这些配置技巧,不仅能有效应对Java面试中关于分库分表的问题,更是构建高性能、可扩展的分布式架构服务的核心能力。在实践中,务必充分测试,并利用好监控和治理功能,确保分片集群的稳定运行。如果你想深入学习更多关于数据库中间件的知识,欢迎到云栈社区交流探讨。




上一篇:Python爬虫核心技能:JSONPath语法详解与实战案例解析
下一篇:C++模板元编程基础:std::true_type与false_type的核心作用与类型萃取实战
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-10 18:55 , Processed in 0.232661 second(s), 39 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表