概述(what)
- 它是一套开源的分布式数据库中间件解决方案,合理的在分布式环境下使用关系型数据库操作。
- 它分为3个部分。分别是ShardingSphere-JDBC、ShardingSphere-Proxy以及未来的ShardingSphere-Sidecar。
- 本文主要介绍ShardingSphere-JDBC
现存的问题(question)
数据库的数量是不可控的,随着时间和业务的发展,会导致表的数据越来越多,这样子对表进行CRUD操作的时候,性能会很慢。
解决的思路(solution)
可以从两个方面来思考,一是硬件层次,增加硬件,例如CPU、内存、硬盘等。但是这种方法治标不治本。二是软件层次,出现上述问题,首先考虑缓存处理、读写分离、使用索引等方式提高性能。最后考虑对数据库进行分表分库操作。而ShardingSphere则是一个优秀的分布式数据库框架,可以简化对分库分表之后数据的相关操作。
分库分表概念(水平垂直搭配分库分表)
分库分表有两种方式,分别是水平切分和垂直切分。基于分库分表操作,又可以分为4种。接下来一一介绍。
- 垂直分表:针对数据库的某张表,将这表中的某些字段存储到另外一张表中。例如:有一张课程表,字段有课程名称、课程封面、课程价格、课程描述信息。将课程名称、课程封面和课程价格作为一张课程基本表中的字段存储,将课程描述信息作为另外一张表的字段存储。
- 垂直分库:针对不同的业务,创建不同的数据库,完成专库专表。例如现有业务是用户下单购买课程。原始思路是将订单表和课程表创建在同一个库中。垂直分库是将订单表和课程表分别存储在不同的数据库中。
- 水平分库:一般是经过了垂直分库以后,数据量仍然很大。根据业务需求,也无法进行垂直分表分库操作。可以将数据分为几个部分,存储在不同的数据库中。原来数据库中有100w行记录,现在将这些数据分别存储在两个数据库中,这样一个数据库中数据只有50w行记录。
- 水平分表:针对一个库中数据库仍然很庞大,可以考虑将数据分开存储在不同的表中。例如原来数据库表中有50w条信息,现在可以分为5张表存储,一张表存储10w的数据量。
ShardingSphere-JDBC
它是一个轻量级的java框架,是增强版的JDBC驱动。主要用于简化数据库分库分表之后的数据的操作。例如:数据库分为2个,要求数据的主键id为奇数增加该记录到 第一个库中,否则增加到第2个库中。之前的思路是根据不同的主键id,从而进行不同的CRUD操作。现在有了ShardingSphere-JDBC,通过配置文件配置,再加上一个正常的CRUD操作,即可完成。
demo解析(水平分表)
1.构建Project,导入依赖
<dependencies>
<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-spring-boot-starter</artifactId>
<version>1.1.20</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.15</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</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>
</dependency>
</dependencies>
2.建数据库,新增实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Course {
private Long cid;
private String cname;
private Long userId;
private String cstatus;
}
3.使用mp对数据库进行操作。
@Mapper
public interface CourseMapper extends BaseMapper<Course> {
}
4.编写配置类
# shardingjdbc分片策略
# 配置数据源,给数据源起名称 水平分表的配置类
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/course_db?serverTimezone=GMT%2B8&useSSL=false
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=123456
#指定course表的分布情况,配置表在哪个数据库里面,表的名称都是什么
# m1.course_1 , m1.course_2
spring.shardingsphere.sharding.tables.course.actual-data-nodes=m1.course_$->{1..2}
#指定course表里面主键cid 生成策略 SNOWFLAKE
spring.shardingsphere.sharding.tables.course.key-generator.column=cid
spring.shardingsphere.sharding.tables.course.key-generator.type=SNOWFLAKE
#指定分片策略 约定cid值偶数添加到course_1表,如果cid是奇数,则添加到course_2表
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}
#打开sql的输出日志
spring.shardingsphere.props.sql.show=true
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true
5.测试
@SpringBootTest
class ShardingjdbcdemoApplicationTests {
//注入Mapper
@Autowired
private CourseMapper courseMapper;
//测试水平分表
@Test
void addCourse1() {
for (int i = 0; i < 10; i++) {
Course course = new Course();
course.setCname("java"+i);
course.setUserId(100L);
course.setCstatus("Normal"+i);
courseMapper.insert(course);
}
}
@Test
void select1(){
QueryWrapper<Course> queryWrapper = new QueryWrapper<>();
//根据自己数据库里面内容进行简单的查询测试
queryWrapper.eq("cid", 709063885038026752L);
Course course = courseMapper.selectOne(queryWrapper);
System.out.println(course.toString());
}
}
水平分库
还是使用那个course类进行测试,只不过创建两个不同的数据库,然后根据cid和user_id的奇偶数来判断存储到哪张表中。
只需要修改application.properties文件即可
# shardingjdbc分片策略
# 配置数据源,给数据源起名称,水平分库+水平分表
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/edu_db_1?serverTimezone=GMT%2B8&useSSL=false
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=123456
#配置第二个数据源的具体内容,包含连接池,驱动,地址,用户名和密码
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/edu_db_2?serverTimezone=GMT%2B8&useSSL=false
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=123456
# 指定数据库分布情况,数据库里面表的分布情况
# m1,m2 course_1 course_2
spring.shardingsphere.sharding.tables.course.actual-data-nodes=m$->{1..2}.course_$->{1..2}
#指定course表里面主键cid 生成策略 SNOWFLAKE
spring.shardingsphere.sharding.tables.course.key-generator.column=cid
spring.shardingsphere.sharding.tables.course.key-generator.type=SNOWFLAKE
#指定表的分片策略 约定cid值偶数添加到course_1表,如果cid是奇数,则添加到course_2表
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}
#指定库分片策略 约定user_id值偶数添加到m1表,如果user_id是奇数,则添加到m2表
spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression=m$->{user_id % 2 + 1}
# 也可以指定库分片策略
#spring.shardingsphere.sharding.tables.course.database-strategy.inline.sharding-column=user_id
#spring.shardingsphere.sharding.tables.course.database-strategy.inline.algorithm-expression=m$->{user_id % 2 + 1}
#打开sql的输出日志
spring.shardingsphere.props.sql.show=true
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true
编写测试代码
//测试水平分库
@Test
void addCourse2() {
Course course = new Course();
course.setCname("javademo1");
//分库根据 user_id
course.setUserId(100L);
course.setCstatus("Normal1");
courseMapper.insert(course);
}
@Test
void select2(){
QueryWrapper<Course> wrapper = new QueryWrapper<>();
//设置 userid 值 own
wrapper.eq("user_id ",100L);
//设置 cid 值 own
wrapper.eq("cid",709073553256873985L);
Course course = courseMapper.selectOne(wrapper);
System.out.println(course);
}
垂直分库
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "t_user") //指定对应表
public class User {
private Long userId;
private String username;
private String ustatus;
}
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
# 配置数据源,给数据源起名称,垂直分库
spring.shardingsphere.datasource.names=m1,m2,m0
#垂直分表
#配置第三个数据源的具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m0.url=jdbc:mysql://localhost:3306/user_db?serverTimezone=GMT%2B8&useSSL=false
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=123456
# 指定数据库分布情况,数据库里面表的分布情况
spring.shardingsphere.sharding.tables.t_user.actual-data-nodes=m$->{0}.t_user
#指定t_user表里面主键user_id 生成策略 SNOWFLAKE
spring.shardingsphere.sharding.tables.t_user.key-generator.column=user_id
spring.shardingsphere.sharding.tables.t_user.key-generator.type=SNOWFLAKE
#指定表的分片策略 约定cid值偶数添加到course_1表,如果cid是奇数,则添加到course_2表
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
测试
//测试垂直分库
@Test
public void add3() {
User user = new User();
user.setUsername("lucy");
user.setUstatus("a");
userMapper.insert(user);
}
@Test
void select3(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.eq("user_id",709083354305134593L);
User user = userMapper.selectOne(wrapper);
System.out.println(user);
}
配置公共表
公共表定义:存储固定数据的表,表数据很少发生变化,查询的时候经常会关联。在每个需要的数据库创建相同结构的表结构。
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName(value = "t_udict")
public class Udict {
private Long dictid;
private String ustatus;
private String uvalue;
}
@Mapper
public interface UdictMapper extends BaseMapper<Udict> {
}
# shardingjdbc分片策略
# 配置数据源,给数据源起名称,公共表配置
spring.shardingsphere.datasource.names=m1,m2,m0
#配置第一个数据源的具体内容,包含连接池,驱动,地址,用户名和密码
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/edu_db_1?serverTimezone=GMT%2B8&useSSL=false
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=123456
#配置第二个数据源的具体内容,包含连接池,驱动,地址,用户名和密码
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/edu_db_2?serverTimezone=GMT%2B8&useSSL=false
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=123456
# 指定数据库分布情况,数据库里面表的分布情况
# m1,m2 course_1 course_2
spring.shardingsphere.sharding.tables.course.actual-data-nodes=m$->{1..2}.course_$->{1..2}
#指定course表里面主键cid 生成策略 SNOWFLAKE
spring.shardingsphere.sharding.tables.course.key-generator.column=cid
spring.shardingsphere.sharding.tables.course.key-generator.type=SNOWFLAKE
#指定表的分片策略 约定cid值偶数添加到course_1表,如果cid是奇数,则添加到course_2表
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}
##指定库分片策略 约定user_id值偶数添加到m1表,如果user_id是奇数,则添加到m2表
#spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column=user_id
#spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression=m$->{user_id % 2 + 1}
# 也可以指定库分片策略
spring.shardingsphere.sharding.tables.course.database-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.course.database-strategy.inline.algorithm-expression=m$->{user_id % 2 + 1}
#打开sql的输出日志
spring.shardingsphere.props.sql.show=true
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true
#垂直分表
#配置第三个数据源的具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m0.url=jdbc:mysql://localhost:3306/user_db?serverTimezone=GMT%2B8&useSSL=false
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=123456
# 指定数据库分布情况,数据库里面表的分布情况
spring.shardingsphere.sharding.tables.t_user.actual-data-nodes=m$->{0}.t_user
#指定t_user表里面主键user_id 生成策略 SNOWFLAKE
spring.shardingsphere.sharding.tables.t_user.key-generator.column=user_id
spring.shardingsphere.sharding.tables.t_user.key-generator.type=SNOWFLAKE
#指定表的分片策略 约定cid值偶数添加到course_1表,如果cid是奇数,则添加到course_2表
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.sharding.broadcast-tables=t_udict
spring.shardingsphere.sharding.tables.t_udict.key-generator.column=dictid
spring.shardingsphere.sharding.tables.t_udict.key-generator.type=SNOWFLAKE
测试
@Test
public void addDict() {
Udict udict = new Udict();
udict.setUstatus("a");
udict.setUvalue("已启用");
udictMapper.insert(udict);
}
@Test
public void deleteDict() {
QueryWrapper<Udict> wrapper = new QueryWrapper<>();
//设置 userid 值
wrapper.eq("dictid",709088530961268737L);
udictMapper.delete(wrapper);
}
读写分离(概念)
为了确保数据库产品的稳定性,很多数据库具备双机热备功能。也就是有2台数据库服务器,一台负责对外的CUD操作,一台负责对外的R操作。主服务器负责处理增、删、改操作。从库负责查询操作,减少数据的访问量。
原理
主从复制:当主服务器有CUD操作时,从服务器自动获取主服务器的操作,并更新自己的从库。
读写分离:主CUD,从R
mysql的主从复制原理是通过binlog日志来完成,主服务器会将CUD操作记录形成binlog日志,从服务器实时监控主服务器,读取并解析日志,完成主从复制。