sharding-jdbc简介
sharding-jdbc是一个轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。
-
适用于任何基于Java的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。
-
基于任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。
-
支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer和PostgreSQL。
sharding-jdbc和Sharding-Proxy和Sharding-Sidecar(计划中)这3款相互独立的产品共同组成了一套开源的分布式数据库中间件解决方案组成的生态圈——ShardingSphere,他们均提供标准化的数据分片、分布式事务和数据库治理功能,可适用于如Java同构、异构语言、容器、云原生等各种多样化的应用场景。详情可见官网:https://shardingsphere.apache.org/
本文主要简单介绍sharding-jdbc的分表用法,集成以及一些注意事项
引入依赖
sharding-jdbc版本:
<dependency>
<groupId>io.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>3.0.0.M1</version>
</dependency>
springBoot版本:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
driud版本:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.10</version>
</dependency>
这里有个版本的坑,试过4.x以及3.x的其他版本,可以正常连接mysql,但是连接oracle时总是会报或这或那的错,或者连接不上数据源,或者url不合法等,不知道是我配置问题还是其他原因,最终尝试了多个版本后确定了以上版本才正常运行起来…
前置准备
这里首先把后续步骤使用的数据库信息,分区表,分区字段等罗列一下,方便后续阅读
- 库名:mall-db
- 数据源名:mall-db
- 逻辑表名:DC_USER_MALL_INFO
- 物理表名:DC_USER_MALL_INFO_202007,DC_USER_MALL_INFO_202008,…
- 分片字段:p_day
sharding-jdbc配置
这里主要采用Yaml形式的配置,除yaml形式外,sharding-jdbc还支持Java配置,springboot的properties文件配置,spring xml命名空间配置共四种配置形式,其余三种配置详见官网配置手册。
yml配置如下:
sharding:
jdbc:
datasource:
names: mall-db # 数据源名称
mall-db: # 与上文names一致
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: oracle.jdbc.driver.OracleDriver
url: jdbc:oracle:thin:@(DESCRIPTION =(ADDRESS_LIST =(ADDRESS = (PROTOCOL = TCP)(HOST = 127.0.0.1)(PORT = 8005)))(CONNECT_DATA =(SERVER = DEDICATED)(SERVICE_NAME = Thinker)))
username: xxxxxx
password: xxxxxxx
config:
sharding:
props:
sql:
show: true #开启真实的SQL显示,否则只能看到逻辑sql
tables:
DC_USER_MALL_INFO: #逻辑表名,数据库中的真实表名为:DC_USER_MALL_INFO_202006,DC_USER_MALL_INFO_202007
actual-data-nodes: mall-db.DC_USER_MALL_INFO_$->{202001..202112} #sharidng 表对应的数据源以及物理名称,根据指定的表达式计算得到需要路由到的物理表名,此表达式表示路由到DC_USER_MALL_INFO_202001至DC_USER_MALL_INFO_202112之间的月表
table-strategy:
standard:
sharding-column: p_day #分片列名,根据该列的值进行数据分片
precise-algorithm-class-name: com.example.radar.mall.core.sharding.PreciseTableShardingAlgorithm #preciseShardingAlgorithm接口的实现类,精确分片,即p_day = 、p_day in()时的分片算法
range-algorithm-class-name: com.example.radar.mall.core.sharding.RangeTableShardingAlgorithm #RangeShardingAlgorithm接口的实现类,范围分片,即p_day between...and...时的分片算法(经测试该版本貌似只支持between...and,并不支持>、<的范围操作)
其他可选配置项详见官网配置手册
PreciseTableShardingAlgorithm实现类:
package com.teradata.radar.mall.core.sharding;
import io.shardingsphere.core.api.algorithm.sharding.PreciseShardingValue;
import io.shardingsphere.core.api.algorithm.sharding.standard.PreciseShardingAlgorithm;
import org.springframework.stereotype.Component;
import java.util.Collection;
/**
* Created by HE31 on 2020/10/29 16:55
*/
@Component
public class PreciseTableShardingAlgorithm implements PreciseShardingAlgorithm<String> {
@Override
public String doSharding(Collection<String> collection, PreciseShardingValue<String> preciseShardingValue) {
String tableName = preciseShardingValue.getLogicTableName();
String key = preciseShardingValue.getValue().substring(0, 6);
String actualTableName = tableName.concat("_").concat(key);
if (collection.contains(actualTableName)){
return actualTableName;
}
throw new UnsupportedOperationException("无效表名:"+actualTableName);
}
}
RangeShardingAlgorithm实现类:
package com.teradata.radar.mall.core.sharding;
import com.google.common.collect.Range;
import io.shardingsphere.core.api.algorithm.sharding.RangeShardingValue;
import io.shardingsphere.core.api.algorithm.sharding.standard.RangeShardingAlgorithm;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Collection;
/**
* Created by HE31 on 2020/10/29 16:55
*/
@Component
public class RangeTableShardingAlgorithm implements RangeShardingAlgorithm<String> {
@Override
public Collection<String> doSharding(Collection<String> collection, RangeShardingValue<String> rangeShardingValue) {
Collection<String> collect = new ArrayList<>();
Range<String> valueRange = rangeShardingValue.getValueRange();
//TODO 这种写法只支持between
String between = rangeShardingValue.getLogicTableName() + "_" + valueRange.lowerEndpoint().substring(0, 6);
String and = rangeShardingValue.getLogicTableName() + "_" +valueRange.upperEndpoint().substring(0, 6);
for (String each : collection) {
if (between.equals(each) || and.equals(each)) {
collect.add(each);
}
}
return collect;
}
}
至此,简单的oracle单库分表配置完成
分表验证
现在进行分表验证,开发一个查询接口,接口内部sql如下:
select count(*) as user_cnt,P_DAY from
DC_USER_MALL_INFO --注意这里为逻辑表名
WHERE MALL_ID=#{mallId} --mall_id可忽略
and p_day between #{startTime} and #{endTime}
GROUP BY P_DAY ORDER BY P_DAY
现查询20200601到20200715时间区间的数据,手工查询DC_USER_MALL_INFO_202006
表结果集如下:
手工查询DC_USER_MALL_INFO_202007表结果集如下:
调用接口查询结果集如下:
[
{
"name": "2020-06-01",
"value": "6462",
"sort": "0"
},
{
"name": "2020-06-02",
"value": "6375",
"sort": "0"
},
{
"name": "2020-06-03",
"value": "1307",
"sort": "0"
},
{
"name": "2020-06-07",
"value": "2474",
"sort": "0"
},
{
"name": "2020-06-08",
"value": "5111",
"sort": "0"
},
{
"name": "2020-06-09",
"value": "5194",
"sort": "0"
},
{
"name": "2020-06-10",
"value": "3199",
"sort": "0"
},
{
"name": "2020-07-04",
"value": "15739",
"sort": "0"
},
{
"name": "2020-07-07",
"value": "6409",
"sort": "0"
},
{
"name": "2020-07-08",
"value": "6207",
"sort": "0"
},
{
"name": "2020-07-09",
"value": "7127",
"sort": "0"
},
{
"name": "2020-07-10",
"value": "8755",
"sort": "0"
},
{
"name": "2020-07-11",
"value": "10676",
"sort": "0"
}
]
结果集正常
日志打印如下:
整合mybatis-plus
mybatis-plus整合与平常springboot引入mybatis-plus无异,这里不再赘述,在使用时,将表名指定为逻辑表名即可
不支持的操作
千万别以为以上一个简单的查询sql测试能正常分表后,就万事大吉了,其实引入sharding-jdbc后,有哪些sql的特性不支持,这才是我们探索阶段最应该关注的,因为分库分表后,就成了一个阉割型的数据库,别分表是成功了,结果项目大半功能不能用,那就得不偿失了。这里以3.x版本特性为例,列举一些不支持的操作。
首先,官网上已经列举了一些JDBC不支持项,详见官网JDBC不支持项
以下是网上冲浪查阅资料整理的一些不支持的操作:
不支持操作 | 解决方案 |
---|---|
distinct | sharding-jdbc不支持distinct,单表可使用group by进行替代。多表联查可使用exists替代 |
having | sharding-jdbc不支持having,可使用嵌套子查询进行替代 |
union | sharding-jdbc不支持union(all),可拆分成多个查询,在程序拼接 |
or | |
insert语句针对分表字段使用SYSDATE()获取数据库系统时间 | |
子查询中出现同样的表 | |
子查询中包含聚合函数 | |
不支持sql中的<!-- – >注释,如必须使用则写在sql前,或使用/* */ | |
不支持text字段 | 改为varchar |
case when | 将逻辑写在程序中 |