ShardingSphere-JDBC入门使用
什么是ShardingSphere?
Apache ShardingSphere 是一款分布式的数据库生态系统, 可以将任意数据库转换为分布式数据库,并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。 它主要由ShardingSphere-JDBC和ShardingSphere-Proxy两个可独立使用、也可混合使用的产品组成。
什么是ShardingSphere-Proxy?
ShardingSphere-Proxy 定位为透明化的数据库代理端,通过实现数据库二进制协议,对异构语言提供支持。 目前提供 MySQL 和 PostgreSQL 协议,透明化数据库操作,对 DBA 更加友好。
- 向应用程序完全透明,可直接当做 MySQL/PostgreSQL 使用;
- 兼容 MariaDB 等基于 MySQL 协议的数据库,以及 openGauss 等基于 PostgreSQL 协议的数据库;
- 适用于任何兼容 MySQL/PostgreSQL 协议的的客户端,如:MySQL Command Client, MySQL Workbench, Navicat 等。
什么是ShardingSphere-JDBC?
ShardingSphere-JDBC 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。
- 适用于任何基于 JDBC 的 ORM 框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template 或直接使用 JDBC;
- 支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, HikariCP 等;
- 支持任意实现 JDBC 规范的数据库,目前支持 MySQL,PostgreSQL,Oracle,SQLServer 以及任何可使用 JDBC 访问的数据库。
准备工作
创建Spring Boot项目,引入MySQL、ShardingSphere-JDBC等依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.2</version>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>创建若干张表,根据表创建controller、service、dao等代码
使用ShardingSphere-JDBC进行读写分离
数据库:master、slave;表:t_user
创建
application.properties
作为基础配置文件1
2
3
4
5
6
7
8
9
10
119090 =
# 使用读写分离配置文件
separate =
demo =
classpath*:**/sql/*.xml =
# 打印sql日志
true =创建
application-separate.properties
作为读写分离配置文件1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40# 数据源名称
master,slave =
# 数据源1(主数据源)
com.alibaba.druid.pool.DruidDataSource =
com.mysql.cj.jdbc.Driver =
jdbc:mysql://host1:3306/practice?useUnicode=true&characterEncoding=utf-8&useSSL=false =
root =
password =
# 数据源2(从数据源)
com.alibaba.druid.pool.DruidDataSource =
com.mysql.cj.jdbc.Driver =
jdbc:mysql://host2:3306/practice?useUnicode=true&characterEncoding=utf-8&useSSL=false =
root =
password =
# spring.shardingsphere.rules.readwrite-splitting.data-sources.<readwrite-splitting-data-source-name>.type=Static
# 读写分离类型,如: Static,Dynamic
Static =
# 写数据源名称
master =
# 读数据源名称,多个从数据源用逗号分隔
slave =
# 负载均衡算法名称
# 自定义轮询算法名称
round_alg =
# 自定义随机算法名称
#spring.shardingsphere.rules.readwrite-splitting.data-sources.my_ds.load-balancer-name=random_alg
# 自定义权重算法名称
#spring.shardingsphere.rules.readwrite-splitting.data-sources.my_ds.load-balancer-name=weight_alg
# spring.shardingsphere.rules.readwrite-splitting.load-balancers.<load-balance-algorithm-name>.type= # ????????
# 负载均衡算法类型
# 使用轮询算法
ROUND_ROBIN =
# 负载均衡算法属性配置
# 权重算法配置
#spring.shardingsphere.rules.readwrite-splitting.load-balancers.weight_alg.props.slave1=1
#spring.shardingsphere.rules.readwrite-splitting.load-balancers.weight_alg.props.slave2=2分别测试查询和插入接口,查看数据源是否正常路由
查询sql路由到从数据源
写入sql路由到主数据源
使用ShardingSphere-JDBC进行分库分表
数据库:ds_0、ds_1;表:t_order_0/1(ds_0/1)、t_order_item_0/1(ds_0/1)
创建
application.properties
作为基础配置文件1
2
3
4
5
6
7
8
9
109090 =
horizontal =
demo =
classpath*:**/sql/*.xml =
# 打印sql日志
true =创建
application-horizontal.properties
作为读写分离配置文件分库分表策略:
分库:订单表根据id对2取模,结果为0,路由到
ds_0
数据库;结果为1,路由到ds_1
数据库。订单明细表根据order_id
对2取模,结果为0,路由到ds_0
数据库;结果为1,路由到ds_1
数据库,即和订单表保持一致分表:订单表、订单明细表根据
user_id
对2取模,结果为0,路由到t_order_0
、t_order_item_0
;结果为1,路由到t_order_1
、t_order_item_1
表1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49# 数据源名称
ds_0,ds_1 =
# 数据源1
com.alibaba.druid.pool.DruidDataSource =
com.mysql.cj.jdbc.Driver =
jdbc:mysql://host1:3306/practice?useUnicode=true&characterEncoding=utf-8&useSSL=false =
root =
password =
com.alibaba.druid.pool.DruidDataSource =
com.mysql.cj.jdbc.Driver =
jdbc:mysql://host2:3306/practice?useUnicode=true&characterEncoding=utf-8&useSSL=false =
root =
password =
# 数据节点,由数据源名 + 表名组成,以小数点分隔。多个表以逗号分隔,支持 inline 表达式。缺省表示使用已知数据源与逻辑表名称生成数据节点
ds_$->{[0,1]}.t_order_$->{0..1} =
ds_$->{[0,1]}.t_order_item_$->{0..1} =
# 分库策略
# 订单表根据id对2取模,路由到ds_0或ds_1数据库
id =
alg_inline_id =
# 订单明细表根据order_id对2取模,路由到ds_0或ds_1数据库,和订单表保持一致
order_id =
alg_inline_order_id =
# 分表策略
# 订单表根据user_id对2取模,路由到t_order_0或t_order_1表
user_id =
alg_mod_id =
# 订单明细表根据user_id对2取模,路由到t_order_item_0或t_order_item_1表
user_id =
alg_mod_id =
# 分片算法
# 订单表,行表达式分片算法
INLINE =
ds_$->{id % 2} =
# 订单表、订单明细表,取模分片算法
MOD =
2 =
# 订单明细表,行表达式分片算法
INLINE =
ds_$->{order_id % 2} =
# 绑定表:分片规则一致的一组分片表。使用绑定表进行多表关联查询时,必须使用分片键进行关联,否则会出现笛卡尔积关联或跨库关联,从而影响查询效率
t_order,t_order_item =编写插入订单和订单行的代码,查看分库分表策略是否生效
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public void mockInsert() {
OrderDO order1 = new OrderDO().setId(snowflake.nextId())
.setOrderNo(snowflake.nextIdStr())
.setUserId(202001L)
.setCreateTime("2023-08-12 17:35:11")
.setCreateTime("2023-08-12 17:35:11")
.setPayAmount(new BigDecimal("10889.0"));
OrderItemDO orderItem1 = new OrderItemDO().setId(snowflake.nextId())
.setOrderId(order1.getId())
.setUserId(202001L)
.setProductName("MacBook Pro")
.setAmount(new BigDecimal("10889.0"));
orderMapper.insert(order1);
orderItemService.insert(orderItem1);
OrderDO order2 = new OrderDO().setId(snowflake.nextId())
.setOrderNo(snowflake.nextIdStr())
.setUserId(202000L)
.setCreateTime("2023-08-12 17:41:54")
.setCreateTime("2023-08-12 17:41:54")
.setPayAmount(new BigDecimal("3899.0"));
OrderItemDO orderItem2 = new OrderItemDO()
.setId(snowflake.nextId())
.setOrderId(order2.getId())
.setUserId(202000L)
.setProductName("iWatch S8")
.setAmount(new BigDecimal("3899.0"));
orderMapper.insert(order2);
orderItemService.insert(orderItem2);
}查看分库分表情况
order_id
为偶数、user_id
为奇数order_id为偶数、user_id为偶数