一、概念:
Sharding-JDBC是一个在客户端的分库分表工具。它是一个轻量级Java框架,在Java的JDBC层提供的额外服务。
ShardingSphere提供标准化的数据分片、分布式事务和数据治理功能。
二、架构图:
- ShardingRuleConfiguration 可以包含多个 TableRuleConfiguration(多张表),也可以设置默认的分库和分表策略。
- 每个TableRuleConfiguration 可以针对表设置 ShardingStrategyConfiguration,包括分库分分表策略。
- ShardingStrategyConfiguration 有 5 种实现(标准、行内、复合、Hint、无)
- ShardingDataSourceFactory利用ShardingRuleConfiguration创建数据源。
三、基础概念:
逻辑表:
order_1、order_2、order_3
真实表:
order
数据节点:
数据分片的最小物理单元。由数据源名称和数据表组成,例: ds_0.order_0
绑定表:
指分片规则一致的主表和子表。例如: order和 t_order
广播表:
指所有的分片数据源中都存在的表,例如,字典表
分片策略:
包含分片键和分片算法;
- 具体策略:
- 标准分片策略(StandardShardingStrategy):用于处理 BETWEEN AND, >, =,
- 复合分片策略(ComplexShardingStrategy):支持多分片键,提供对 SQL 语句中的 =, >, =,
- 行表达式分片策略(InlineShardingStrategy): t_user_$->{u_id % 8} 表示 t_user 表根据 u_id 模 8,而分成 8 张表,表名称为 t_user_0 到 t_user_7
- Hint分片策略(HintShardingStrategy):通过 Hint 指定分片值而非从 SQL 中提取分片值的方式进行分片的策略
- 不分片策略(NoneShardingStrategy)
- 分片键:用于分片的表字段
- 分片算法:
- 精确分片算法(单一分片键,例如 =、in)
- 范围分片算法(单一分片键,例如 ETWEEN AND、>、=、
- 复合分片算法(多片键)
- Hint分片算法
主键生成策略:
通过在客户端生成自增主键替换以数据库原生自增主键的方式,做到分布式主键无重复。
四、执行流程:
SQL 解析 => 查询优化 => SQL路由 => SQL改写 => SQL执行 => 结果归并
五、使用方法:
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
# pom.xml
<!--sharding-jdbc-->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.1</version>
</dependency>
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
# 自定义分片规则,需要Java SPI进行加载。
/**
* 〈一句话功能简述〉<br>
* 〈客户端用户表id自增生成〉
*
* @author hanxinghua
* @create 2022/6/10
* @since 1.0.0
*/
@Component
public class AppUserIdGenerator implements ShardingKeyGenerator {
private RedisUtil redisUtil;
@Override
public Comparable<?> generateKey() {
if (redisUtil == null) {
synchronized (this) {
if (redisUtil == null) {
redisUtil = SpringUtil.getBean("redisUtil");
}
}
}
return redisUtil.incr(AppUtil.APP_USER_ID, 1L);
}
@Override
public String getType() {
return "APPUSERID";
}
@Override
public Properties getProperties() {
return null;
}
@Override
public void setProperties(Properties properties) {
}
}
Java SPI 地址:
src/main/resources/META-INF/services/org.apache.shardingsphere.spi.keygen.ShardingKeyGenerator
ShardingKeyGenerator内容:
com.hippo.online.config.shardingjdbc.AppUserIdGenerator
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
# 自定义分片规则
/**
* 〈一句话功能简述〉<br>
* 〈用户表分片规则〉
*
* @author hanxinghua
* @create 2022/6/13
* @since 1.0.0
*/
public class AppUserShardingAlgorithm implements PreciseShardingAlgorithm<Integer> {
@Override
public String doSharding(Collection<String> targetTableNames, PreciseShardingValue<Integer> shardingValue) {
String value = String.valueOf(shardingValue.getValue());
for (String targetTableName : targetTableNames) {
if (targetTableName.endsWith(value)) {
return targetTableName;
}
}
return "app_user_999";
}
}
/**
* 〈一句话功能简述〉<br>
* 〈用户记录表分片规则〉
*
* @author hanxinghua
* @create 2022/6/13
* @since 1.0.0
*/
public class AppUserRecordShardingAlgorithm implements PreciseShardingAlgorithm<Integer> {
@Override
public String doSharding(Collection<String> targetTableNames, PreciseShardingValue<Integer> shardingValue) {
String value = String.valueOf(shardingValue.getValue());
for (String targetTableName : targetTableNames) {
if (targetTableName.endsWith(value)) {
return targetTableName;
}
}
return "app_user_record_999";
}
}
---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ---- ----
# yaml配置:
spring:
shardingsphere:
# 数据源配置
datasource:
names: ds0
ds0:
type: com.alibaba.druid.pool.DruidDataSource
driverClassName: com.mysql.cj.jdbc.Driver
... ...
props:
sql.show: true # 打开sql输出日志
sharding:
defaultDataSourceName: ds0 # 默认数据源名称
tables:
# 逻辑表名称
app_user:
# 数据节点:数据源$->{0..N}.逻辑表名$->{0..N}
actualDataNodes: ds0.app_user_$->{999..1011} # 现在是只分表不分库
# 主键生成规则
keyGenerator:
column: id
type: APPUSERID # 自定义
# 拆分表策略
tableStrategy:
standard:
shardingColumn: app_id
preciseAlgorithmClassName: com.config.shardingjdbc.AppUserShardingAlgorithm
# 逻辑表名称
app_user_record:
actualDataNodes: ds0.app_user_record_$->{999..1011} # 现在是只分表不分库
tableStrategy:
standard:
shardingColumn: app_id
preciseAlgorithmClassName: com.config.shardingjdbc.AppUserRecordShardingAlgorithm
# 绑定表
bindingTables: app_user,app_user_record
# 表达式举例:
## ${begin..end}:表示范围区间
## ${[unit1, unit2, unit_x]}:表示枚举值
## db1.table_$->{[1,2]}
## t_user_$->{u_id % 8}:表示t_user表根据u_id模8,而分成8张表,表名称为t_user_0到t_user_7
# 主键生成规则举例:
key-generator:
column: id
type: SNOWFLAKE #雪花算法
props:
work.id: 0 # 表示雪花id机器标识位,取值范围[0,1024)
max.vibration.offset: 2 # 在高并发下,每次生成id都可能跨毫秒,夸毫秒,则序列部分会从0开始计算,每次生成的id都是偶数,奇偶分片有问题
# 用于单分片键的标准分片举例:
spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.standard.sharding-column= #分片列名称
spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.standard.precise-algorithm-class-name= #精确分片算法类名称,用于=和IN。该类需实现PreciseShardingAlgorithm接口并提供无参数的构造器
spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.standard.range-algorithm-class-name= #范围分片算法类名称,用于BETWEEN,可选。该类需实现RangeShardingAlgorithm接口并提供无参数的构造器
# 用于多分片键的复合分片举例:
spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.complex.sharding-columns= #分片列名称,多个列以逗号分隔
spring.shardingsphere.sharding.tables.<logic-table-name>.database-strategy.complex.algorithm-class-name= #复合分片算法类名称。该类需实现ComplexKeysShardingAlgorithm接口并提供无参数的构造器
六、多数据源配置:
# pom.xml:
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.2.1</version>
</dependency>
# 配置多数据源:
## 设置严格模式,默认false(不启动)。启动后在未匹配到指定数据源时候回抛出异常,不启动会使用默认数据源。
spring.datasource.dynamic.strict=true
## 设置默认的数据源或者数据源组,默认值即为master
spring.datasource.dynamic.primary=master
## 配置master数据源:
### 以下datasource相关,省略部分
spring.datasource.dynamic.datasource.master.type=
spring.datasource.dynamic.datasource.master.driverClassName =
... ...
## 配置shardingsphere相关数据源:
### 以下datasource相关,省略部分
spring.shardingsphere.datasource.names=ds-1
spring.shardingsphere.datasource.ds-1.type=
spring.shardingsphere.datasource.ds-1.driverClassName=
... ...
spring.shardingsphere.datasource.names=ds-2
spring.shardingsphere.datasource.ds-2.type=
spring.shardingsphere.datasource.ds-2.driverClassName=
... ...
# 数据源配置类:
/**
* 〈一句话功能简述〉<br>
* 〈配置数据源〉
*
* @author hanxinghua
* @create 2022/6/16
* @since 1.0.0
*/
@Configuration
@AutoConfigureBefore({DynamicDataSourceAutoConfiguration.class, SpringBootConfiguration.class})
public class DataSourceConfig {
/**
* 分表数据源名称
*/
public static final String SHARDING_DATA_SOURCE_NAME = "sharding";
/**
* 动态数据源配置项
*/
@Autowired
private DynamicDataSourceProperties dynamicDataSourceProperties;
@Lazy
@Resource
private DataSource shardingDataSource;
@Bean
public DynamicDataSourceProvider dynamicDataSourceProvider() {
Map<String, DataSourceProperty> datasourceMap = dynamicDataSourceProperties.getDatasource();
return new AbstractDataSourceProvider() {
@Override
public Map<String, DataSource> loadDataSources() {
Map<String, DataSource> dataSourceMap = createDataSourceMap(datasourceMap);
// 将Sharding-jdbc管理的数据源也交给动态数据源管理
dataSourceMap.put(SHARDING_DATA_SOURCE_NAME, shardingDataSource);
return dataSourceMap;
}
};
}
@Bean
@Primary
public DataSource dataSource(DynamicDataSourceProvider dynamicDataSourceProvider) {
DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
dataSource.setPrimary(dynamicDataSourceProperties.getPrimary());
dataSource.setStrict(dynamicDataSourceProperties.getStrict());
dataSource.setStrategy(dynamicDataSourceProperties.getStrategy());
dataSource.setProvider(dynamicDataSourceProvider);
dataSource.setP6spy(dynamicDataSourceProperties.getP6spy());
dataSource.setSeata(dynamicDataSourceProperties.getSeata());
return dataSource;
}
}
# 使用:
访问没有分表的数据时使用默认的普通数据源,访问分表的数据时使用@DS("sharding")注解
七、数据迁移:
1.停机迁移(不推荐)
2.数据双写迁移
3.采用canal中间件迁移