基于Spring Boot JPA 集成 Sharding JDBC 实践教程

项目需求:项目每日约有70万单,运行了近四个月,已经有九千多万条数据,单表存储经常会出现事务异常,查询统计慢等问题, 自然就有了按月动态分表的需求(无须分库)。

分库分表相关的技术,目前主流的有两种。一种是在服务端,如Mycat中间件;另一种在客户端,如Sharding JDBC。(注意:这里的服务端、客户端是相对我们的应用而言)
我这里主讲Sharding JDBC,进入正题前,我们先来看一下Sharding-JDBC的整体架构图:
Sharding-JDBC的整体架构图
废话少说,进入正题

引入Sharding JDBC jar 包

我使用的版本是:

<dependency>
			<groupId>org.apache.shardingsphere</groupId>
		    <artifactId>sharding-jdbc</artifactId>
		    <version>4.0.0</version>
		    <type>pom</type>
</dependency>
<druid.version>1.1.21</druid.version>

注意版本的搭配,尽量都使用最新的稳定版,否则可能会有不兼容的问题

配置分表策略

分表算法 SingleKeyDynamicModuloTableShardingAlgorithm:

/**
 * @FileName SingleKeyDynamicModuloTableShardingAlgorithm.java
 * @Description: 
 *
 * @Date 2020年2月13日 
 * @author LiYiShi
 * 
 */

@RequiredArgsConstructor
public class SingleKeyDynamicModuloTableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Date> {

	private final String tablePrefix;
	
	@Override
	public String doEqualSharding(Collection<String> availableTargetNames, ShardingValue<Date> shardingValue) {
		SimpleDateFormat formatter = new SimpleDateFormat("yyyyMM");
        return tablePrefix +formatter.format(shardingValue.getValue());
	}

	@Override
	public Collection<String> doInSharding(Collection<String> availableTargetNames, ShardingValue<Date> shardingValue) {
		// TODO Auto-generated method stub
		Collection<String> result = new LinkedHashSet<>(shardingValue.getValues().size());
		SimpleDateFormat formatter = new SimpleDateFormat("yyyyMM");
		for (Date value : shardingValue.getValues()) {
			result.add(tablePrefix + formatter.format(value));
		}
		return result;
	}

	@Override
	public Collection<String> doBetweenSharding(Collection<String> availableTargetNames, ShardingValue<Date> shardingValue) {
		// TODO Auto-generated method stub
		Collection<String> result = new LinkedHashSet<>();
		DateFormat sdf = new SimpleDateFormat("yyyyMM");
		Range<Date> ranges = shardingValue.getValueRange();
		Date startTime = ranges.lowerEndpoint();
		 Date endTime = ranges.upperEndpoint();
		 Calendar cal = Calendar.getInstance();
		 while (startTime.getTime()<=endTime.getTime()){
			 result.add(tablePrefix + sdf.format(startTime));
			 cal.setTime(startTime);//设置起时间
			 cal.add(Calendar.MONTH,1);
			 startTime = cal.getTime();
		 }
		return result;
	}

}

给数据源配置分库分表的策略,这里我采用的是硬编码的方式。当然你也可以配置在yml文件里

    @Bean(name = "dtsDataSource")
	@Qualifier("dtsDataSource")
	public DataSource dtsDataSource() {
		// 1.设置分库映射
		Map<String, DataSource> dataSourceMap = new HashMap<>(2);
		DruidDataSource dtsDataSource = new DruidDataSource();
		//这里会自动注入数据库的连接配置,如url、username、password等
		dtsDbProperties.getDruidDataSource(dtsDataSource);
		dataSourceMap.put("dtsDataSource", dtsDataSource);
		// 设置默认db为dtsDataSource,也就是为那些没有配置分库分表策略的指定的默认库
		// 如果只有一个库,也就是不需要分库的话,map里只放一个映射就行了,只有一个库时不需要指定默认库,
		// 但2个及以上时必须指定默认库,否则那些没有配置策略的表将无法操作数据
		DataSourceRule rule = new DataSourceRule(dataSourceMap, "dtsDataSource");
		// 2.设置分表映射
		TableRule requestTableRule = TableRule.builder("dts_jps_request")
				.tableShardingStrategy(new TableShardingStrategy("timestamp", 
						new SingleKeyDynamicModuloTableShardingAlgorithm("dts_jps_request_")))
				.dataSourceRule(rule).dynamic(true).build();
		
		TableRule policyTableRule = TableRule.builder("dts_jps_policy")
				.tableShardingStrategy(new TableShardingStrategy("createdtime", 
						new SingleKeyDynamicModuloTableShardingAlgorithm("dts_jps_policy_")))
				.dataSourceRule(rule).dynamic(true).build();
		
		
		TableRule policyAttachTableRule = TableRule.builder("dts_jps_policy_attach")
				.tableShardingStrategy(new TableShardingStrategy("createdtime", 
						new SingleKeyDynamicModuloTableShardingAlgorithm("dts_jps_policy_attach_")))
				.dataSourceRule(rule).dynamic(true).build();
		
		ShardingRule shardingRule = ShardingRule.builder().dataSourceRule(rule)
		.tableRules(Arrays.asList(requestTableRule, policyTableRule, policyAttachTableRule))
		.build();
		// 创建数据源
		DataSource dataSource = ShardingDataSourceFactory.createDataSource(shardingRule);
		customDataSources.put("dtsDataSource", dataSource);
		return dataSource;
	}

到这里整个分表的策略配置就完成了,你会发现复杂的分表场景,用框架来解决会非常简单。

动态建表

上面完成了程序里的动态路由表,但是我们的数据库表并没有动态的创建。你可以一次性创建未来一年的表,也可以用定时任务定时提前创建下个月的表。建表可参考如下sql:

-- ****** 年月,在程序里动态替换
CREATE TABLE IF NOT EXISTS `dts_jps_policy_******` LIKE `dts_jps_policy`

分表之后的查询

按照上面的实现方式,分表之后的查询需要注意一点:查询条件必须包含有分片键,否则会报异常。其他就正常开发就行,开发上和没分片之前没啥区别

总结

到此动态按月分表的需求就开发完成了,整体来说还比较简单,重点在于能够合理的根据自身的业务需求去实现自己的分表逻辑

Sharding JDBC官网

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值