目录
- 一、ShardingSphere简介
- 二、ShardingSphere-分库分表
- 1、垂直拆分
- (1)垂直分库
- (2)垂直分表
- 2、水平拆分
- (1)水平分库
- (2)水平分表
- 三、水平分库操作
- 1、创建数据库和表
- 2、配置分片的规则
- 3、测试类
- 四、水平分表操作
- 1、引入依赖
- 2、创建数据库、表
- 3、分片策略
- 4、编写代码实现
- (1)实体类
- (2)编写Mapper
- (3)启动类修改(添加扫描mapper的入口)
- (4)测试类(采用springboot给我们提供的)
- 五、垂直分库/分表操作
- 1、创建数据库、表
- 2、分片策略
- 3、编写代码实现
- (1)创建User实体类
- (2)Mapper
- 4、测试
- 六、公共表
- 1、概念
- 2、实现
- (1)在三个数据库中创建公共表
- (2)公共表的配置
- (3)测试代码
- 3、相关问题及解决方法
- (1)Caused by: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'null' in 'class com.chenxin.shardingsphere.entity.Udict'
一、ShardingSphere简介
Apache ShardingSphere 是一个开源的分布式数据库中间件解决方案组成的生态圈,且它的产品有Sharding-JDBC和Sharding-Proxy组成(他们两个之间是独立的),同时又能混合部署(组合起来一起使用)。它们都提供了标准化的数据分片、分布式事务和数据库的治理能力,可适用如Java、云原生开发的应用场景。
ShardingSphere定位是关系型数据库中间件,目的是充分为了合理地在分布式的场景下利用关系型数据库的计算能力和存储能力,而不是实现一个全新的关系型数据库。
二、ShardingSphere-分库分表
1、垂直拆分
(1)垂直分库
垂直分库:把单一的数据库按照业务的不同进行划分(专库专表)
(2)垂直分表
操作数据库中的某张表,我们把这张表里面的一部分字段拿出来存储到一张新的表里,剩下的字段放在另一张表里。
2、水平拆分
(1)水平分库
水平分库相当于把数据库水平切割,原来一个表中的数据可能会分配到不同的数据库中,这就是水平分库。
(2)水平分表
水平分表就是把表中的数据进行了水平切割,意味着按照行进行切割,也就是说不同行的数据被切割后可能在不同的表中。
三、水平分库操作
1、创建数据库和表
-- 创建两个数据库
CREATE DATABASE edu_db_1;
CREATE DATABASE edu_db_2;
-- 需要在两个数据库中都执行下面的脚本
CREATE TABLE course_1(
cid BIGINT(20) PRIMARY KEY,
cname VARCHAR(20) NOT NULL,
user_id BIGINT(20) NOT NULL,
cstatus VARCHAR(20) NOT NULL
);
CREATE TABLE course_2(
cid BIGINT(20) PRIMARY KEY,
cname VARCHAR(20) NOT NULL,
user_id BIGINT(20) NOT NULL,
cstatus VARCHAR(20) NOT NULL
);
2、配置分片的规则
spring.shardingsphere.datasource.names=ds-1,ds-2
spring.shardingsphere.datasource.ds-1.jdbc-url=jdbc:mysql://localhost:3306/edu_db_1?serverTimezone=Asia/Shanghai&useSSL=false
spring.shardingsphere.datasource.ds-1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds-1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds-1.username=root
spring.shardingsphere.datasource.ds-1.password=root
spring.shardingsphere.datasource.ds-2.jdbc-url=jdbc:mysql://localhost:3306/edu_db_2?serverTimezone=Asia/Shanghai&useSSL=false
spring.shardingsphere.datasource.ds-2.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds-2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds-2.username=root
spring.shardingsphere.datasource.ds-2.password=root
spring.main.allow-bean-definition-overriding=true
spring.shardingsphere.sharding.tables.course.actual-data-nodes=ds-$->{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.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=user_id
spring.shardingsphere.sharding.tables.course.database-strategy.inline.algorithm-expression=ds-$->{user_id % 2 + 1}
spring.shardingsphere.props.sql.show=true
3、测试类
// ------------- 测试分库 ---------------------
@Test
void addCorseFpdb() {
Course course = new Course();
course.setCname("java01");
// 根据user_id进行分库,偶数是在edu_db_1,基数是在edu_db_2
course.setUserId(100L);
course.setCstatus("已启用");
courseMapper.insert(course);
}
四、水平分表操作
1、引入依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.17</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0</version>
</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>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2、创建数据库、表
create table course_1(
cid bigint(20) primary key,
cname varchar(20) not null,
user_id bigint(20) not null,
cstatus varchar(20) not null
);
create table course_2(
cid bigint(20) primary key,
cname varchar(20) not null,
user_id bigint(20) not null,
cstatus varchar(20) not null
);
3、分片策略
spring.shardingsphere.datasource.names=ds-0
spring.shardingsphere.datasource.ds-0.jdbc-url=jdbc:mysql://localhost:3306/course_db?serverTimezone=Asia/Shanghai&useSSL=false
spring.shardingsphere.datasource.ds-0.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds-0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds-0.username=root
spring.shardingsphere.datasource.ds-0.password=root
spring.main.allow-bean-definition-overriding=true
spring.shardingsphere.sharding.tables.course.actual-data-nodes=ds-0.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.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
4、编写代码实现
(1)实体类
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class Course {
private Long cid;
private String cname;
private Long userId;
private String cstatus;
}
(2)编写Mapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chenxin.shardingsphere.entity.Course;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface CourseMapper extends BaseMapper<Course> {
}
(3)启动类修改(添加扫描mapper的入口)
@SpringBootApplication
@MapperScan("com.chenxin.shardingsphere.mapper")
public class ShardingsphereApplication {
public static void main(String[] args) {
SpringApplication.run(ShardingsphereApplication.class, args);
}
}
(4)测试类(采用springboot给我们提供的)
@SpringBootTest
class ShardingsphereApplicationTests {
@Resource
CourseMapper courseMapper;
@Test
void addCourse() {
Course course = null;
for (int i = 0; i < 100; i++) {
course = new Course();
course.setCname("Java");
course.setUserId(1000L);
course.setCstatus("Nor1");
courseMapper.insert(course);
}
}
}
五、垂直分库/分表操作
1、创建数据库、表
CREATE DATABASE user_db;
USE user_db;
CREATE TABLE T_USER(
user_id BIGINT(20) NOT NULL PRIMARY KEY,
username VARCHAR(100) NOT NULL,
ustatus VARCHAR(50) NOT NULL
);
2、分片策略
spring.shardingsphere.datasource.names=ds-1,ds-2,ds-3
spring.shardingsphere.datasource.ds-1.jdbc-url=jdbc:mysql://localhost:3306/edu_db_1?serverTimezone=Asia/Shanghai&useSSL=false
spring.shardingsphere.datasource.ds-1.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds-1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds-1.username=root
spring.shardingsphere.datasource.ds-1.password=root
spring.shardingsphere.datasource.ds-2.jdbc-url=jdbc:mysql://localhost:3306/edu_db_2?serverTimezone=Asia/Shanghai&useSSL=false
spring.shardingsphere.datasource.ds-2.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds-2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds-2.username=root
spring.shardingsphere.datasource.ds-2.password=root
spring.shardingsphere.datasource.ds-3.jdbc-url=jdbc:mysql://localhost:3306/user_db?serverTimezone=Asia/Shanghai&useSSL=false
spring.shardingsphere.datasource.ds-3.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.ds-3.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.ds-3.username=root
spring.shardingsphere.datasource.ds-3.password=root
spring.main.allow-bean-definition-overriding=true
# spring.shardingsphere.sharding.tables.course.actual-data-nodes=ds-$->{1..2}.course_$->{1..2}
spring.shardingsphere.sharding.tables.t_user.actual-data-nodes=ds-$->{3}.t_user
spring.shardingsphere.sharding.tables.t_user.key-generator.column=user_id
spring.shardingsphere.sharding.tables.t_user.key-generator.type=SNOWFLAKE
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.t_user.table-strategy.inline.algorithm-expression=t_user
spring.shardingsphere.props.sql.show=true
3、编写代码实现
(1)创建User实体类
import lombok.Data;
import lombok.ToString;
// 实体类添加注解
@TableName(value = "t_user")
@Data
@ToString
public class User {
private Long userId;
private String username;
private String ustatus;
}
(2)Mapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chenxin.shardingsphere.entity.User;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
4、测试
// ------------- 测试垂直分库 ----------------------
@Test
void addUser() {
User user = new User();
user.setUsername("chenxin");
user.setUstatus("a");
userMapper.insert(user);
}
六、公共表
1、概念
存储固定数据的表,表里面的数据很少发生变化,查询的时候经常进行关联查询
在每个数据库中创建出相同的结构的表(我们将每个数据库中共有的表成为公共表)
2、实现
(1)在三个数据库中创建公共表
CREATE TABLE t_udict(
dicid bigint(20) primary key,
ustatus varchar(20) not null,
uvalue varchar(100) not null
);
(2)公共表的配置
spring.shardingsphere.sharding.broadcast-tables=t_udict
spring.shardingsphere.sharding.tables.t_udict.key-generator.column=dicid
spring.shardingsphere.sharding.tables.t_udict.key-generator.type=SNOWFLAKE
(3)测试代码
创建实体类
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
@TableName(value = "t_udict")
public class Udict {
private Integer dicid;
private String ustatus;
private String uvalue;
}
Mapper
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.chenxin.shardingsphere.entity.Udict;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface UdictMapper extends BaseMapper<Udict> {
}
测试代码
@Test
void addDict() {
Udict udict = new Udict();
udict.setUstatus("0");
udict.setUvalue("已启用");
udictMapper.insert(udict);
}
@Test
void updateDict() {
Udict udict = new Udict();
udict.setDicid(792033627201339393L);
udict.setUstatus("1");
udict.setUvalue("未启用");
udictMapper.updateById(udict);
}
3、相关问题及解决方法
(1)Caused by: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named ‘null’ in ‘class com.chenxin.shardingsphere.entity.Udict’
问题原因
数据库中字段名和实体类中相差太大,程序自己对应不上
解决方法
// 需要在实体类指定主键是哪一个属性
@Data
@ToString
@TableName(value = "t_udict")
public class Udict {
@TableId
private Long dicid;
private String ustatus;
private String uvalue;
}