一、前言
ShardingSphere-JDBC 是 Apache ShardingSphere 的第一个产品,现已更名为
ShardingSphere
。 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。
在学习
Sharding-JDBC
分库分表之前,我们有必要先了解分库分表的一些知识。
分库分表
一般的机器(4核16G),单库的MySQL并发(QPS+TPS)超过了2k,系统基本就完蛋了。最好是并发量控制在1k左右。这里就引出一个问题,为什么要分库分表?
分库分表目的:解决高并发,和数据量大的问题。
分库分表的维度有两个:垂直和水平
- 水平拆分 :把一个库或者一个表的数据,按照时间,地区或者某个业务键划分到不同的表或者不同的库中,拆分过后每个库表结构是一致的
- 垂直拆分 :把一个大表拆分成多个小表,每个库表的结构都不一样,从而达到冷热字段拆分,也可以按照业务进行垂直分库,如分成用户库,订单库,拆分过后每个库表都只包含部分字段
一般公司都会按照上图的模式进行拆分,先按照业务进行垂直分库,然后再根据需要分表的库进行水平拆分。
二、核心概念
1、分片
一般我们在提到分库分表的时候,大多是以水平切分模式(水平分库、分表)为基础来说的,而分片的可以理解为用于数据库(表)水平拆分的数据库字段,这个字段就叫做分片键,SQL 中如果无分片字段,将执行全路由,性能较差
2、逻辑表or真实表
逻辑表非真实存在的表,如一个订单表order,select * from order,这里的order指的就是逻辑表,并不存在,如果我们将order拆分成order_1,order_2....order_n,其中order_n才是真实存在的物理表
3、数据节点
数据分片的最小单元,它由数据源名称和数据表组成,例如上图中
order_db_1.t_order_0
就表示一个数据节点。
4、分布式主键
数据分⽚后,不同数据节点⽣成全局唯⼀主键是⾮常棘⼿的问题,同⼀个逻辑表(
t_order
)内的不同真实表(
t_order_n
)之间的⾃增键由于⽆法互相感知而产⽣重复主键,而sharding 内置了
UUID
、
SNOWFLAKE
两种分布式主键⽣成器,默认使⽤雪花算法(
snowflake
)⽣成64bit的⻓整型数据。不仅如此它还抽离出分布式主键⽣成器的接口,⽅便我们实现⾃定义的⾃增主键⽣成算法。
5、分片策略&分片算法
分片策略只是抽象出的概念,它是由分片算法和分片健组合而成,分片算法做具体的数据分片逻辑。从执行 SQL 的角度来看,分库分表可以看作是一种路由机制,把 SQL 语句路由到我们期望的数据库或数据表中并获取数据,分片算法可以理解成一种路由规则。
分片策略:
- 标准分片策略(StandardShardingStrategy) 仅支持单分片键,可提供对SQL语句中的=, >, <, >=, <=, IN和BETWEEN AND的分片操作支持。
- 复合分片策略(ComplexShardingStrategy) 与标准分片一致,但支持多分片键
- 行表达式分片策略(InlineShardingStrategy) 使用Groovy的表达式,提供对SQL语句中的=和IN的分片操作支持,只支持单分片键
- Hint分片策略 (HintShardingStrategy) 强制路由到某库某表,此策略无需配置分片键,但需要通过外部
HintManager
指定分库、分表信息 - 不分片策略(NoneShardingStrategy)
分片算法:
- 精确分片算法(PreciseShardingAlgorithm) 用于处理使用单一键作为分片键的=与IN进行分片的场景。需要配合StandardShardingStrategy使用,且是Standard策略必须算法。
- 范围分片算法(RangeShardingAlgorithm) 用于处理使用单一键作为分片键的BETWEEN AND、>、<、>=、<=进行分片的场景。需要配合StandardShardingStrategy使用,是Standard策略可选算法。
- 复合分片算法(ComplexKeysShardingAlgorithm) 用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合ComplexShardingStrategy使用。
- Hint分片算法(HintShardingAlgorithm) 用于处理使用Hint行分片的场景。需要配合HintShardingStrategy使用。
值得注意的是,sharding只提供了算法的接口,具体算法的实现还需要我们自己去实现。
列举了怎么多的分片策略,分片算法,大家一定很懵,下面来看一些具体的案例
三、配置
sharding提供了4种配置方式,java API、YAML、Spring Boot Starter、Spring命名空间,用于不同的使用场景。 通过配置,应用开发者可以灵活的使用数据分片、读写分离、数据加密、影子库等功能,并且能够叠加使用。
了解了核心概念,首先我们分别建立两个数据库,每个库包含两张表,如下图
下面我们通过YAML和java API两种方式来测试
1、YAML
YAML分库分表的配置(注意不同版本的sharding配置属性略有差异,详情参加官方文档)本文采用4.x版本
server:
port: 8085
spring:
shardingsphere:
# 参数配置,显示sql
props:
sql:
show: true
# 配置数据源
datasource:
# 给每个数据源取别名,下面的ds1,ds1任意取名字
names: ds0,ds1
# 给master-ds1每个数据源配置数据库连接信息
ds0:
# 配置druid数据源
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/ksd_user?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
username: root
password: 123456
maxPoolSize: 100
minPoolSize: 5
# 配置ds1-slave
ds1:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3307/ksd_user?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
username: root
password: 123456
maxPoolSize: 100
minPoolSize: 5
sharding:
# 配置分表的规则
tables:
# ksd_user 逻辑表名
ksd_user:
# 数据节点:数据源$->{0..N}.逻辑表名$->{0..N}
actual-data-nodes: ds$->{0..1}.ksd_user$->{0..1}
# 拆分库策略,也就是什么样子的数据放入放到哪个数据库中。
database-strategy:
inline:
sharding-column: sex # 分片字段(分片键)
algorithm-expression: ds$->{sex % 2} # 分片算法表达式
# 拆分表策略,也就是什么样子的数据放入放到哪个数据表中。
table-strategy:
inline:
sharding-column: age # 分片字段(分片键)
algorithm-expression: ksd_user$->{age % 2} # 分片算法表达式
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.lb.shardingtest.entity
我们一段段来解释
spring:
shardingsphere:
# 配置数据源
datasource:
# 给每个数据源取别名,下面的ds0,ds1任意取名字
names: ds0,ds1
# 给每个数据源配置数据库连接信息
ds0:
# 配置druid数据源
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/ksd_user?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
username: root
password: 123456
maxPoolSize: 100
minPoolSize: 5
# 配置ds1-slave
ds1:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3307/ksd_user?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
username: root
password: 123456
maxPoolSize: 100
minPoolSize: 5
这里配置了两个库(数据源)并分别取了别名ds0和ds1
sharding:
# 配置分表的规则
tables:
# ksd_user 逻辑表名
ksd_user:
# 数据节点:数据源$->{0..N}.逻辑表名$->{0..N}
actual-data-nodes: ds$->{0..1}.ksd_user$->{0..1}
# 拆分库策略,也就是什么样子的数据放入放到哪个数据库中。
database-strategy:
inline:
sharding-column: sex # 分片字段(分片键)
algorithm-expression: ds$->{sex % 2} # 分片算法表达式
# 拆分表策略,也就是什么样子的数据放入放到哪个数据表中。
table-strategy:
inline:
sharding-column: age # 分片字段(分片键)
algorithm-expression: ksd_user$->{age % 2} # 分片算法表达式
其中ksd_user为要分片表的逻辑表名,使用的分库和分表策略都为inline,也是是上文所说的行表达式分片策略(InlineShardingStrategy),数据节点分别为:
ds0.ksd_user0 ds0.ksd_user1 ds1.ksd_user0 ds1.ksd_user1
其中分库算法的分片列为sex(假设男为0,女为1),那么男通过取模会得到ds0库,女会得到ds1库,然后再根据分表算法的分片列age取模再次选择放到哪一张表。
测试数据为:sex为男(0) 年龄偶数(20)预期应该插入ds0.ksd_user0
测试结果:符合预期
2、java API
现在我们再来通过 java API这种方式来实现同样的分库分表策略,因为是单个分片键,所以我们的算法只需实现PreciseShardingAlgorithm接口即可
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import org.springframework.stereotype.Component;
import java.util.Collection;
/**
* 分表算法
* @date 2022/6/7
**/
@Component
public class MyAgeAlgorithm implements PreciseShardingAlgorithm<Integer> {
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Integer> preciseShardingValue) {
//分片列值
Integer value = preciseShardingValue.getValue();
String shardingValue = String.valueOf(value % 2);
for (String targetName : availableTargetNames) {
if(targetName.endsWith(shardingValue)) {
return targetName;
}
}
return null;
}
}
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import org.springframework.stereotype.Component;
import java.util.Collection;
/**
* 分库算法
* @date 2022/6/7
**/
@Component
public class MySexAlgorithm implements PreciseShardingAlgorithm<Integer> {
/**
*
* @param availableTargetNames 实际库别名或者表名集合
* @param preciseShardingValue 分片列 其中包含3个属性:
* columnName 分片列 value 分片列值 logicTableName 逻辑表名
*
* @return
*/
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Integer> preciseShardingValue) {
//分片列值
Integer value = preciseShardingValue.getValue();
String shardingValue = String.valueOf(value % 2);
for (String targetName : availableTargetNames) {
if(targetName.endsWith(shardingValue)) {
return targetName;
}
}
return null;
}
}
然后修改一下配置文件的分库分表策略即可
database-strategy:
standard:
sharding-column: sex # 分片字段(分片键)
preciseAlgorithmClassName: com.lb.shardingtest.sharding.algorithm.MySexAlgorithm
# 拆分表策略,也就是什么样子的数据放入放到哪个数据表中。
table-strategy:
standard:
sharding-column: age # 分片字段(分片键)
preciseAlgorithmClassName: com.lb.shardingtest.sharding.algorithm.MyAgeAlgorithm
测试数据为:sex为女(1) 年龄奇数(20)预期应该插入ds1.ksd_user1
测试结果:符合预期
参考文章:
概览 :: ShardingSphere
【学相伴】ShardingJDBC最新完整教程IDEA版通俗易懂 | KuangStudy | 飞哥 | 狂神说 | 学相伴_哔哩哔哩_bilibili
初识-Sharding-JDBC_天亮i的博客-CSDN博客_sharding-jdbc
Sharding-JDBC分片策略详解(二)
版权归原作者 dotaer-df 所有, 如有侵权,请联系我们删除。