shardingsphere(2)—部分实战篇
2、ShardingSphere
2.1、什么是ShardingSphere?
地址:https://shardingsphere.apache.org/index_zh.html
ShardingJDBC:
shardingJDBC定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。它使⽤客户端直连数据库,以 jar 包形式提供服务,⽆需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。
ShardingProxy
ShardingProxy定位为透明化的数据库代理端,提供封装了数据库⼆进制协议的服务端版本,⽤于完成对异构语⾔的⽀持。⽬前提供 MySQL 和 PostgreSQL 版本,它可以使⽤任何兼容 MySQL/PostgreSQL 协议的访问客⼾端。
两种方式的区别:
Sharding-JDBC | Sharding-Proxy | |
---|---|---|
数据库 | 任意 | MySQL/PostgreSQL |
连接消耗数 | 高 | 低 |
异构语言 | 仅java | 任意 |
性能 | 损耗低 | 损耗略高 |
无中心化 | 是 | 否 |
静态入口 | 无 | 有 |
ShardingJDBC只是客户端的一个工具包,可以理解为一个特殊的JDBC驱动包,所有分库分表逻辑均由业务方自己控制,所以他的功能相对灵活,支持的数据库也非常多,但是对业务侵入大,需要业务方自己定制所有的分库分表逻辑。
ShardingProxy是一个独立部署的服务,对业务方无侵入,业务方可以像用一个普通的MySQL服务一样进行数据交互,基本上感觉不到后端分库分表逻辑的存在,但是这也意味着功能会比较固定,能够支持的数据库也比较少。
2.2、核心概念:
shardingjdbc的核心功能是数据分片和读写分离,通过ShardingJDBC,应用可以透明的使用JDBC访问已经分库分表、读写分离的多个数据源,而不用关心数据源的数量以及数据如何分布。
-
逻辑表:水平拆分的数据库的相同逻辑和数据结构表的总称
-
真实表:在分片的数据库中真实存在的物理表。
-
数据节点:数据分片的最小单元。由数据源名称和数据表组成
-
绑定表:分片规则一致的主表和子表。
-
广播表:也叫公共表,指素有的分片数据源中都存在的表,表结构和表中的数据在每个数据库中都完全一致。例如字典表。
-
分片键:用于分片的数据库字段,是将数据库(表)进行水平拆分的关键字段。SQL中若没有分片字段,将会执行全路由,性能会很差。
-
分片算法:通过分片算法将数据进行分片,支持通过=、BETWEEN和IN分片。分片算法需要由应用开发者自行实现,可实现的灵活度非常高。
-
分片策略:真正用于进行分片操作的是分片键+分片算法,也就是分片策略。在ShardingJDBC中一般采用基于Groovy表达式的inline分片策略,通过一个包含分片键的算法表达式来制定分片策略,如t_user_$->{u_id%8}标识根据u_id模8,分成8张表,表名称为t_user_0到t_user_7。
2.3、分库分表实战
pom依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.zhz</groupId>
<artifactId>sharding-sphere-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>sharding-sphere-demo</name>
<description>Spring Boot集成ShardingSphere做分库分表</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.1.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
pojo实体类
package com.zhz.shardingspheredemo.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Description: 课程实体类
*
* @author zhouhengzhe
* @date 2021/9/4下午3:24
* @since
*/
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "course")
public class Course {
@TableId(value = "cid")
private Long cid;
@TableField(value = "cname")
private String cname;
@TableField(value = "user_id")
private Long userId;
@TableField(value = "cstatus")
private String cstatus;
}
mapper
package com.zhz.shardingspheredemo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.zhz.shardingspheredemo.pojo.Course;
/**
* @Description: 课程mapper数据操作层
*
* @author zhouhengzhe
* @date 2021/9/4下午3:24
* @since
*/
public interface CourseMapper extends BaseMapper<Course> {
}
启动类
package com.zhz.shardingspheredemo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @author mac
*/
@SpringBootApplication
@MapperScan("com.zhz.shardingspheredemo.mapper")
public class ShardingSphereDemoApplication {
public static void main(String[] args) {
SpringApplication.run(ShardingSphereDemoApplication.class, args);
}
}
2.3.1、分表不分库
coursedb库有两个表
CREATE TABLE course_1 (
cid BIGINT(20) PRIMARY KEY,
cname VARCHAR(50) NOT NULL,
user_id BIGINT(20) NOT NULL,
cstatus varchar(10) NOT NULL
);
CREATE TABLE course_2 (
cid BIGINT(20) PRIMARY KEY,
cname VARCHAR(50) NOT NULL,
user_id BIGINT(20) NOT NULL,
cstatus varchar(10) NOT NULL
);
2.3.1.1、配置文件
#配置数据源
spring.shardingsphere.datasource.names=m1
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/coursedb?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=root
# 要生成多少张真实表(#配置真实表分布)
spring.shardingsphere.sharding.tables.course.actual-data-nodes=m1.course_$->{1..2}
# 表的主键是什么(主键生成策略)
spring.shardingsphere.sharding.tables.course.key-generator.column=cid
spring.shardingsphere.sharding.tables.course.key-generator.type=SNOWFLAKE
spring.shardingsphere.sharding.tables.course.key-generator.props.worker.id=1
#配置分表策略
spring.shardingsphere.sharding.tables.course.table-strategy.inline.sharding-column=cid
spring.shardingsphere.sharding.tables.course.table-strategy.inline.algorithm-expression=course_$->{cid%2+1}
#其他运行属性
spring.shardingsphere.props.sql.show=true
# 通过注册与现有定义同名的定义,设置是否允许覆盖 bean 定义 默认false
spring.main.allow-bean-definition-overriding=true
解释:
- 1、首先定义一个数据源m1,并对m1进行实际的JDBC参数配置
- 2、spring.shardingsphere.sharding.tables.course开头的一系列属性即定义了一个名为course的逻辑表。
- actual-data-nodes属性即定义course逻辑表的实际数据分布情况,他分布在m1.course_1和m1.course_2两个表。
- key-generator属性配置了他的主键列以及主键生成策略。
- ShardingJDBC默认提供了UUID和SNOWFLAKE两种分布式主键生成策略。
- table-strategy属性即配置他的分库分表策略。分片键为cid属性。分片算法为course_$->{cid%2+1},表示按照cid模2+1的结果,然后加上前面的course__ 部分作为前缀就是他的实际表结果。注意,这个表达式计算出来的结果需要能够与实际数据分布中的一种情况对应上,否则就会报错。
- sql.show属性表示要在日志中打印实际SQL
2.3.1.2、测试类
package com.zhz.shardingspheredemo;
import com.zhz.shardingspheredemo.mapper.CourseMapper;
import com.zhz.shardingspheredemo.pojo.Course;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ShardingSphereDemoApplicationTests {
@Resource
private CourseMapper courseMapper;
@Test
public void testSharding() {
for (int i = 0; i < 10; i++) {
Course course=Course
.builder()
.cname("shardingSphere")
.userId((long)(1000+i))
.cstatus("1")
.build();
courseMapper.insert(course);
}
}
}
2.3.1.3、运行结果
2.3.2、奇偶分表分库
coursedb,coursedb2库都各自有两个相同的表
CREATE TABLE course_1 (
cid BIGINT(20) PRIMARY KEY,
cname VARCHAR(50) NOT NULL,
user_id BIGINT(20) NOT NULL,
cstatus varchar(10) NOT NULL
);
CREATE TABLE course_2 (
cid BIGINT(20) PRIMARY KEY,
cname VARCHAR(50) NOT NULL,
user_id BIGINT(20) NOT NULL,
cstatus varchar(10) NOT NULL
);
2.3.2.1、配置文件:
#配置数据源
spring.shardingsphere.datasource.names=m1,m2
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/coursedb?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=root
#配置数据源
spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m2.url=jdbc:mysql://localhost:3306/coursedb2?useUnicode=true&characterEncoding=utf8&useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=root
# 要生成多少张真实表(#配置真实表分布)
spring.shardingsphere.sharding.tables.course.actual-data-nodes=m$->{1..2}.course_$->{1..2}
# 表的主键是什么(主键生成策略)
spring.shardingsphere.sharding.tables.course.key-generator.column=cid
spring.shardingsphere.sharding.tables.course.key-generator.type=SNOWFLAKE
spring.shardingsphere.sharding.tables.course.key-generator.props.worker.id=1
#配置分表策略
spring.shardingsphere.sharding.tables.course.table-strategy.inline.sharding-column=cid
spring.shardingsphere.sharding.tables.course.table-strategy.inline.algorithm-expression=course_$->{cid%2+1}
# 配置分库策略
spring.shardingsphere.sharding.tables.course.database-strategy.inline.sharding-column=cid
spring.shardingsphere.sharding.tables.course.database-strategy.inline.algorithm-expression=m$->{cid%2+1}
#其他运行属性
spring.shardingsphere.props.sql.show=true
# 通过注册与现有定义同名的定义,设置是否允许覆盖 bean 定义 默认false
spring.main.allow-bean-definition-overriding=true
2.3.2.2、测试类
package com.zhz.shardingspheredemo;
import com.zhz.shardingspheredemo.mapper.CourseMapper;
import com.zhz.shardingspheredemo.pojo.Course;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
@RunWith(SpringRunner.class)
@SpringBootTest
public class ShardingSphereDemoApplicationTests {
@Resource
private CourseMapper courseMapper;
@Test
public void testSharding() {
for (int i = 0; i < 10; i++) {
Course course=Course
.builder()
.cname("shardingSphere")
.userId((long)(1000+i))
.cstatus("1")
.build();
courseMapper.insert(course);
}
}
}
2.3.2.3、运行结果
我们会发现course的course_1里面和course2里面的course_2里面有数据!!
测试查询:
@Test
public void testQuery(){
QueryWrapper<Course> queryWrapper=new QueryWrapper<>();
//成功
// queryWrapper.eq("cid",1434444620292460546L);
//成功
// queryWrapper.orderByAsc("cid");
queryWrapper.between("cid",1434444618337914881L,1434444620539924482L);
List<Course> courses = courseMapper.selectList(queryWrapper);
courses.forEach(System.out::println);
}
发现不能范围查询,因为inline是根据分区键玩的!!
- 点赞
- 收藏
- 关注作者
评论(0)