结合上一篇docker部署的mysql主从, 本篇主要讲解SpringBoot项目结合Sharding-JDBC如何实现分库分表、读写分离。

一、Sharding-JDBC介绍

1、这里引用官网上的介绍:

  定位为轻量级Java框架,在Java的JDBC层提供的额外服务。 它使用客户端直连数据库,以jar包形式提供服务,无需额外部署和依赖,可理解为增强版的JDBC驱动,完全兼容JDBC和各种ORM框架。

  适用于任何基于JDBC的ORM框架,如:JPA, Hibernate, Mybatis, Spring JDBC Template或直接使用JDBC。

  支持任何第三方的数据库连接池,如:DBCP, C3P0, BoneCP, Druid, HikariCP等。

  支持任意实现JDBC规范的数据库。目前支持MySQL,Oracle,SQLServer,PostgreSQL以及任何遵循SQL92标准的数据库。

2、自己的理解:

  增强版的JDBC驱动,客户端使用的时候,就像正常使用JDBC驱动一样, 引入Sharding-JDBC依赖包,连接好数据库,配置好分库分表规则,读写分离配置,然后客户端的sql 操作 Sharding-JDBC会自动根据配置完成 分库分表和读写分离操作。


二、实现效果

1、下图展示了我们通过Sharding-JDBC实现的分库分表及读写分离效果图

  分库分表:结合上一篇的主从,这里我们使用上次搭建的主从数据库,3307的app1是主数据库,3308的app1是对应的从数据库。同时,我们在3307新建app2库和user2表,这里的app2库需要和app1库一样,user2表和user1表结构一样,主从会自动帮我们建表同步到3308,然后我们在项目中使用Sharding-JDBC 配置响应的分库分表策略,使得插入数据的时候 根据配置字段的分片规则将数据打入对应的库和表。在我们这里主要是 根据分库的分片规则决定数据进入3307的app1库还是app2库,然后再根据分表的分片规则决定进入user1表还是user2表。

  读写分离:读写分离 在我们这里主要指的是 我们项目DQL会根据Sharding-JDBC配置的master-slave-rule走的3308的数据源,而项目的DML会根据master-slave-rule走3307的数据源

三、Spring-Boot项目整合Sharding-JDBC实现分库分表、读写分离

1、这里创建一个maven项目,首先引入依赖,pom.xml文件如下。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
</parent> <groupId>com.cgg</groupId>
<artifactId>sharding-jdbc-test</artifactId>
<version>1.0-SNAPSHOT</version> <properties>
<java.version>1.8</java.version>
</properties> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency> <dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency> <dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.21</version>
</dependency> <dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.1</version>
</dependency> <dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.1.1</version>
</dependency> <dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency> <dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency> </dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build> </project>

注意:这里使用的是4.0的sharding-jdbc,spring-boot的版本是2.x的,在整合过程中遇见了许多问题,后面会有错误的解决步骤。

2、application.yml文件如下

spring:
jpa:
properties:
hibernate:
hbm2ddl:
auto: create
dialect: org.hibernate.dialect.MySQL5Dialect
show_sql: true
shardingsphere:
props:
sql:
show: true
datasource:
names: master0,master0slave0,master1,master1slave0
master0:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3307/app1?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
username: root
password: 654321
master1:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3307/app2?useUnicode=true&characterEncoding=utf8&tinyInt1isBit=false&useSSL=false&serverTimezone=GMT
username: root
password: 654321
master0slave0:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3308/app1?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT
username: root
password: 654321
master1slave0:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3308/app2?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT
username: root
password: 654321
sharding:
default-database-strategy:
inline:
sharding-column: id
algorithm-expression: app$->{(id % 2)+1}
tables:
user:
actual-data-nodes: app$->{1..2}.user$->{1..2}
table-strategy:
inline:
sharding-column: id
algorithm-expression: user$->{((""+id)[2..10].toInteger() % 2)+1}
key-generator:
column: id
type: SNOWFLAKE
master-slave-rules:
app1:
master-data-source-name: master0
slave-data-source-names: master0slave0
app2:
master-data-source-name: master1
slave-data-source-names: master1slave0
sharding:
jdbc:
config:
masterslave:
load-balance-algorithm-type: random

3、application.properties文件

spring.main.allow-bean-definition-overriding=true

mybatis-plus.mapper-locations= classpath:/mapper/*.xml

mybatis-plus.configuration.log-impl= org.apache.ibatis.logging.stdout.StdOutImpl

4、分库分表实现

4.1、先说下数据源,结合之前mysql主从的文章,我本地127.0.0.1:3307端口是主,127.0.0.1:3308端口是从。

   在3307下建立两个库app1和app2,同时每个库里面建立两张表user1和user2表,用来完成分库分表。

   下面是app1库SQL语句:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0; -- ----------------------------
-- Table structure for user1
-- ----------------------------
DROP TABLE IF EXISTS `user1`;
CREATE TABLE `user1` (
`id` bigint(11) NOT NULL COMMENT '主键id',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; -- ----------------------------
-- Table structure for user2
-- ----------------------------
DROP TABLE IF EXISTS `user2`;
CREATE TABLE `user2` (
`id` bigint(11) NOT NULL COMMENT '主键id',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; SET FOREIGN_KEY_CHECKS = 1;

   下面是app2库SQL语句:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0; -- ----------------------------
-- Table structure for user1
-- ----------------------------
DROP TABLE IF EXISTS `user1`;
CREATE TABLE `user1` (
`id` bigint(11) NOT NULL COMMENT '主键id',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = DYNAMIC; -- ----------------------------
-- Table structure for user2
-- ----------------------------
DROP TABLE IF EXISTS `user2`;
CREATE TABLE `user2` (
`id` bigint(11) NOT NULL COMMENT '主键id',
`name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic; SET FOREIGN_KEY_CHECKS = 1;

4.2、这里我们解释一下配置的分库分表规则实现将数据插入到app1和app2库,user1和user2表

    sharding:
default-database-strategy:
inline:
sharding-column: id #分片的字段是id主键
algorithm-expression: app$->{(id % 2)+1} #分片的算法是 id对2求余然后加1
tables:
user:
actual-data-nodes: app$->{1..2}.user$->{1..2} #实际的数据节点是(app1/app2).(user1/user2)
table-strategy:
inline:
sharding-column: id #分表的分片字段是主键id
algorithm-expression: user$->{((""+id)[2..10].toInteger() % 2)+1} #分表的算法是取id的2-10位对2求余然后加1
key-generator:
column: id # 自动生成主键
type: SNOWFLAKE # 生成主键的规则是雪花算法

   上面配置的规则指的是,当有数据要插入数据库,或者进行查询时,sharding-jdbc通过分片配置的字段id的值,去根据配置的算法 进行运算,得到结果,例如上述分库规则,拿到id值 对2求余加1,那么不管id怎么变化算法返回的值永远是1和2,即app$->{(id % 2)+1} 对应的就是app1和app2库,分表的规则是同样道理。

   说明:这里只是配置了简单的分片规则来演示sharding-jdbc如何完成分库分表,我们也可以使用代码重写

方法来实现更复杂的分片策略。最后,这里的$->{(id % 2)+1} 的{}中实际上是一个Groovy语法的表达式,sharding-jdbc是通过Groovy语法糖来解析分片策略的。所以想要配置更为复杂的策略,建议学一下Groovy语法。

4.3、接下来我们介绍配置的读写分离规则,如何实现读写分离

      master-slave-rules:
app1: #分区 app1
master-data-source-name: master0 #分区 app1的主数据源
slave-data-source-names: master0slave0 #分区 app1的从数据源
app2: #分区 app2
master-data-source-name: master1 #分区 app2的主数据源
slave-data-source-names: master1slave0 #分区 app2的从数据源

   上面读写分离的规则指的是,分区app1的主从数据源,分区app2的主从数据源。至于这里的分区为什么是app1和app2?这里说明一下,我自己配置的时候,配置了几次都没有成功,一开始参照官网手册配置,以为分区名称可以自定义,于是配置的是ds0和ds1,但是项目启动报错了。报错信息是:

Cannot find data source in sharding rule, invalid actual data node is: 'app1.user1'

开始以为是使用的sharding-jdbc版本问题,但是换了版本还是有问题,于是开始调试了一下源码:

   

    从上面的截图中很明显就能发现,这里是要判断我们配置的分区集合也就是ds0和ds1是否包含 实际节点的数据源名称,也就是数据库名称。所以这里的分区名称是和我们上面配置的分片策略的数据库名称有关系的。

4.4、验证

    接下来我们验证实际的效果。这里贴一下单元测试的代码。

/**
* @author cgg
* @version 1.0.0
* @date 2021/10/25
*/
@SpringBootTest(classes = ShardingJdbcApp.class)
@RunWith(SpringRunner.class)
public class AppTest { @Resource
private IUserService userService; /**
* 测试sharding-jdbc添加数据
*/
@Test
public void testShardingJdbcInsert() { userService.InsertUser();
} /**
* 测试sharding-jdbc查询数据
*/
@Test
public void testShardingJdbcQuery() { //全部查询
userService.queryUserList(); //根据指定条件查询
userService.queryUserById(1452619866473324545L); } } /**
* @author cgg
* @version 1.0.0
* @date 2021/10/25
*/
@Service
@Slf4j
public class UserServiceImpl implements IUserService { @Resource
private UserMapper userMapper; @Resource
private DataSource dataSource; @Override
public List<User> queryUserList() {
List<User> userList = userMapper.queryUserList();
userList.forEach(user -> System.out.println(user.toString()));
return userList;
} @Override
public User queryUserById(Long id) {
User user = userMapper.selectOne(Wrappers.<User>lambdaQuery().eq(User::getId, id));
System.out.println(user.toString());
return user;
} @Override
public void InsertUser() {
for (int i = 20; i < 40; i++) {
User user = new User();
user.setName("XX-" + i);
int count = userMapper.insert(user);
System.out.println(count);
}
} }

    4.4.1、首先看全部查询的结果

    4.4.2、再看下单条查询的结果

    4.4.3、再看下新增结果(实际插入到了主数据源的app1库user1表,并且后续每条插入都是走的主数据源,没有slave的操作)


四、问题及总结

   到这里sharding-jdbc的初步基本使用已经没问题了,除了最简单的使用,我们还需要考虑分库分表后事务怎么处理,即分布式事务问题,还有读写分离后,数据不一致及同步延时问题,这些就需要我们从概念理论学习,然后在结合实际业务考虑方案,后续会接着这篇文章先出一篇sharding-proxy的使用。再出一篇关于分布式事务和主从数据延时的博客。

Sharding-JDBC基本使用,整合Springboot实现分库分表,读写分离的更多相关文章

  1. 分库分表(7)--- SpringBoot+ShardingSphere实现分库分表 + 读写分离

    分库分表(7)--- ShardingSphere实现分库分表+读写分离 有关分库分表前面写了六篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理 ...

  2. 分库分表(6)--- SpringBoot+ShardingSphere实现分表+ 读写分离

    分库分表(6)--- ShardingSphere实现分表+ 读写分离 有关分库分表前面写了五篇博客: 1.分库分表(1) --- 理论 2.分库分表(2) --- ShardingSphere(理论 ...

  3. ShardingJdbc-分表;分库;分库分表;读写分离;一主多从+分表;一主多从+分库分表;公共表;数据脱敏;分布式事务

    目录 创建项目 分表 导包 表结构 Yml 分库 Yml Java 分库分表 数据库 Yml 读写分离 数据库 Yml 其他 只请求主库 读写分离判断逻辑代码 一主多从+分表 Yml 一主多从+分库分 ...

  4. DB层面上的设计 分库分表 读写分离 集群化 负载均衡

    第1章  引言 随着互联网应用的广泛普及,海量数据的存储和访问成为了系统设计的瓶颈问题.对于一个大型的 互联网应用,每天几十亿的PV无疑对数据库造成了相当高的负载.对于系统的稳定性和扩展性造成了极大的 ...

  5. MySQL+MyCat分库分表 读写分离配置

    一. MySQL+MyCat分库分表 1 MyCat简介 java编写的数据库中间件 Mycat运行环境需要JDK. Mycat是中间件.运行在代码应用和MySQL数据库之间的应用. 前身 : cor ...

  6. Windows环境下使用Mycat模拟分库分表-读写分离案例

    一.基本环境 W7 64位.Mycat1.6.MySQL8.0 二.Mycat核心配置文件配置 解压Mycat1.6,并对server.xml.schema.xml.rule.xml三个核心配置文件做 ...

  7. 【ShardingSphere技术专题】「ShardingJDBC」SpringBoot之整合ShardingJDBC实现分库分表(JavaConfig方式)

    前提介绍 ShardingSphere介绍 ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC.Sharding-Proxy和Shardin ...

  8. springboot(十三)-分库分表-手动配置

    sharding-jdbc简介 Sharding-JDBC直接封装JDBC API,可以理解为增强版的JDBC驱动,旧代码迁移成本几乎为零: 可适用于任何基于java的ORM框架,如:JPA, Hib ...

  9. springboot+jpa分库分表项目实例

    分库分表场景 关系型数据库本身比较容易成为系统瓶颈,单机存储容量.连接数.处理能力都有限.当单表的数据量达到1000W或100G以后,由于查询维度较多,即使添加从库.优化索引,做很多操作时性能仍下降严 ...

随机推荐

  1. 【第五篇】-Maven 构建配置文件之Spring Cloud直播商城 b2b2c电子商务技术总结

    Maven 构建配置文件 构建配置文件是一系列的配置项的值,可以用来设置或者覆盖 Maven 构建默认值. 使用构建配置文件,你可以为不同的环境,比如说生产环境(Production)和开发(Deve ...

  2. POJ3625Building Roads

    Building Roads Description Farmer John had just acquired several new farms! He wants to connect the ...

  3. 用 shell 脚本做命令行工具扩展

    问题的提出 公司开发机与远程服务器之间有严格的隔离策略,不能直接使用 ssh 登录,而必需通过跳板机.这样一来,本地与服务器之间的一些文件传输变得非常不便.经过咨询,运维教了我一招: $ nc -l ...

  4. GoLang设计模式07 - 责任链模式

    责任链模式是一种行为型设计模式.在这种模式中,会为请求创建一条由多个Handler组成的链路.每一个进入的请求,都会经过这条链路.这条链路上的Handler可以选择如下操作: 处理请求或跳过处理 决定 ...

  5. k8s-PodApi对象

    init容器 pod的生命周期钩子 资源限制 podApi对象概览 apiVersion + kind  一个是版本 一个是资源组  共同确定当前yaml由谁来管理 metadata元数据 用来唯一标 ...

  6. CF1556D-Take a Guess【交互】

    正题 题目链接:https://codeforces.com/contest/1556/problem/D 题目大意 现在有\(n\)个你不知道的数字,你有两种询问操作 询问两个下标的数字的\(and ...

  7. P5405-[CTS2019]氪金手游【树形dp,容斥,数学期望】

    前言 话说在\(Loj\)下了个数据发现这题的名字叫\(fgo\) 正题 题目链接:https://www.luogu.com.cn/problem/P5405 题目大意 \(n\)张卡的权值为\(1 ...

  8. idea使用gitee的小坑

    1. 账号配置 账号配置登陆时提示 *** is not a valid login name: Email support only. 翻译:只能支持邮箱登录 解决方法:在gitee网站上查看自己配 ...

  9. pymysql链接时,密码含有特殊符号。

    类如含有@之类的特殊符号,在链接数据库时,需要提前url转码,不然会报密码错误. python3/2分别引用是同样的第三方库,但是引用方式不同 python2 from urllib import q ...

  10. 踩坑系列《七》解决VMware安装完成之后,不能联网的问题

    成功安装CentOS 6.5 好之后,它是默认并不能联网,这时候得需要对root用户进行网络设置 1.先登录root账户 2.命令行输入以下命令,修改配置文件 vim /etc/sysconfig/n ...