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的数据分片策略配置主要围绕四个核心方面:数据源配置、分片规则定义、分片算法选择、高级特性配置。
- 数据源配置:首先定义多个物理数据源,配置连接池参数。这是分片的基础。
- 分片规则定义:
- 分片表配置:通过
actual-data-nodes 指定实际数据节点
- 分片策略:分为分库策略和分表策略,支持标准、复合、Hint等多种策略
- 绑定表配置:优化关联查询,避免笛卡尔积
- 广播表配置:配置需要在所有分片都存在的小表
- 分片算法选择:
- 行表达式算法:适合简单取模分片
- 哈希取模算法:数据分布均匀
- 时间范围算法:适合按时间分片
- 自定义算法:满足复杂业务需求
- 高级特性配置:
- 分布式主键:配置Snowflake等算法
- 读写分离:与分片规则结合使用
- 数据加密:透明化字段加密
- 动态配置:基于ZooKeeper/Nacos的动态管理
实战经验:
在我们电商项目中,订单表按 user_id % 8 分库,按 order_id % 32 分表,采用绑定表优化订单查询。配置时特别注意了广播表如商品类目表的同步,并通过Hint机制支持热点订单的特殊处理。”
常见追问及应对
-
如何选择分片算法?
- 答:根据业务特点:离散数据用哈希取模,时间序列用范围分片,复杂业务用自定义算法。还要考虑扩容时的数据迁移成本。
-
绑定表有什么作用?
- 答:避免关联查询产生笛卡尔积。例如订单表和订单明细表绑定后,JOIN查询只会关联相同分片的数据,性能提升显著。
-
如何配置才能平滑扩容?
- 答:使用一致性哈希算法减少数据迁移;预留分片容量;配置双写迁移方案;通过治理中心动态更新配置。
-
生产环境配置有哪些注意事项?
- 答:关闭SQL日志显示;启用健康检查和监控;配置连接池参数;使用集群模式保证高可用;有完整的回滚方案。
-
如何验证分片配置是否正确?
- 答:编写单元测试验证路由逻辑;使用
sql-show: true 检查实际执行的SQL;通过监控观察数据分布是否均匀。
总结
配置 ShardingSphere 的数据分片策略是一项系统工程,需要结合具体业务场景、数据量、增长模式和访问模式来综合设计。从基础的取模分片到高级的动态治理,从简单的 YAML 配置到复杂的编程式控制,ShardingSphere 提供了丰富的工具来应对各种挑战。掌握这些配置技巧,不仅能有效应对Java面试中关于分库分表的问题,更是构建高性能、可扩展的分布式架构服务的核心能力。在实践中,务必充分测试,并利用好监控和治理功能,确保分片集群的稳定运行。如果你想深入学习更多关于数据库中间件的知识,欢迎到云栈社区交流探讨。