目录
前言
好记性不如烂笔头
1. ShardingSphere
ShardingSphere官网:http://shardingsphere.apache.org
什么是 ShardingSphere ,它有什么好处?它能做什么事情?它有什么特点?
- Apache ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC、Sharding-Proxy和Sharding-Sidecar(规划中)这3款相互独立,却又能够混合部署配合使用的产品组成。它们均提供标准化的数据分片、分布式事务、数据库治理功能,可适用于如Java同构、异构语言、云原生等各种多样化的应用场景。
- ShardingSphere定位为关系型数据库中间件,旨在充分合理地在分布式的场景下利用关系型数据库计算和存储能力,而并非实现一个全新的关系型数据库。它通过关注不变,进而抓住事务本质。关系型数据库当今依然占有巨大市场,是各个公司核心业务的基石,未来也难于撼动,我们目前阶段更加关注在原有基础上的增量,而非颠覆。
- ShardingSphere已经在2020年4月16日成为Apache顶级项目(Apache官方发布从4.0.0版本开始)
归纳重点:
1.一套开源的分布式数据库中间件解决方案
2.有三个产品:Sharding-JDBC 和 Sharding-Proxy、Sharding-Sidecar,学习前两个
3.定位为关系型数据库中间件,合理在分布式环境下使用关系型数据库操作
2. 分库分表
背景:X网是一个电商平台,可以买手机,在查询手机页面,显示很多手机,包括对应的商家,对应关系型数据库一个是商品表,一个是商家表。随着时间的推移和业务量的增加,商家和手机也会也越来越多,数据库中的数据也会越来越多 ,商品表和商家表的数据量也会越来越大,就算增加从表处理或者索引的处理,并不能从根本上解决性能的降低。数据量一多的情况下,在进行CURD(创建--Create、更新--Update、读取--Retrieve、删除--Delete)操作时性能会有很大的问题,这个时候对这个问题进行相应的处理。
解决方案:
方案1: 增加服务器资源,提高服务器性能,如CPU、内存、磁盘空间
缺点:没有从根本上解决问题。①成本很高②数据量在不断增加,不可能每天都去加服务器资源
方案2:进行分库分表
步骤:为商品表和商家表分别建一个数据库,商品DB和商家DB, 再对商品表进行拆分,分为商品表1和商品表2,每张表存1万或者10万数据(有个数量的限制),同样对商家表也进行拆分,分为商家表1和商家表2,有个固定的数据量,当有新的数据增加,再增加新表,这是简单的分库分表处理。
好处:原来一张表中有上千万的数据甚至更多的数据,分库分表后,无形中进行分担,每张表的数据量急剧减少,这样做极大的提高了性能。让 多个库分担一个库的压力,让多张表分担一张大表的压力。
为什么要分库分表?
数据库数据量不可控的,随着时间和业务发展,造成表里面数据越来越多,如果再去对数据库表 curd 操作时候,造成性能问题。
什么是分库分表?
把数据库拆分成若干个数据库,把大表拆分若干个小表组成。让原来的单一库单一表数据量变小,极大的提高数据库的性能。
解决了什么问题?
为了解决由于数据量过大而造成数据库性能降低问题。
3. 分库分表的方式
分库分表有两种方式:垂直切分和水平切分
垂直切分:垂直分表和垂直分库
水平切分:水平分表和水平分库
3.1. 垂直分表
X网是个在线教育平台,查询课程时,课程列表显示很多课程,在数据库中,会把课程信息存到课程表中。课程列表页面只展示基本信息(课程名称、课程封面),点击某一课程进入详情页面会展示描述信息(课程描述、课程其它信息等)。随着时间的推移,课程会越来越多,这些信息会存在课程表中,课程表的数据量可能是几千万甚至上亿,在进行CURD操作时,操作效率会特别低,所以要对他进行分库分表处理。
什么是垂直分表?
操作数据库中某张表,把这张表中一部分字段数据存到一张新表里面,再把这张表另一部分字段数据存到另外一张表里面。
好处:
查询时,在课程列表页面只查询课程基本信息,查课程基本信息表就可以了,不需要查询课程描述表,减少了IO占用。
修改时,如果按照之前,在修改课程描述时,会进行锁表处理,课程名称、课程封面、课程价格等字段也会被锁住,影响了查询效率 。 垂直分表后,课程名称、课程封面、课程价格等字段不会受到影响。
3.2. 垂直分库
垂直分表的局限性:分了多张表,数据量还在同一个数据库中,在查询的时候,会IO量增大,数据库的压力会增大。
什么是垂直分库?
把单一数据库按照业务进行划分,做到专库专表。
例子:如果课程是收费的,在课程详情页面,点击购买时,会涉及到两张表,课程表和订单表。在同一个数据库中,在进行查询时,会造成大量的IO产生,不利于查询效率的提高,课程表可以拆分,订单表不好拆分,在这基础上把库进行拆分。把课程表放到课程数据库中,把订单表放到订单数据库中,这两个库放到不同的服务器上,做到专库专用。
3.3. 水平分库
垂直分表、垂直分库的局限性:随着业务量的增加,表中的数据也会急剧增加,比如课程表有几千万或者几亿的数据量。做了垂直分库、分表后,大表数据量任然很大,在操作数据库的时候,性能会很低。
什么时水平分库?
把一个数据库拆分成两个或多个相同结构的数据库,减少单库单表的压力。
数据该怎么存?
添加课程时,通过课程ID来做判断,当课程ID是偶数,加到课程数据库A中,当课程ID是奇数,加到课程数据库B中,然后依次往不同库中相同表中添加数据。
3.4. 水平分表
垂直分表、垂直分库、水平分库的局限性:不管一个库或者多个库,里面只有一张表,还会产生单表数据量过大的问题。如果对单表数据量过大问题进行水平分库,因为不同的数据库放到不同的服务器上,这样维护起来不方便。
什么叫水平分表?
同一个数据库中,创建相同结构的多张表,每张表放数据量的一部分,
水平切分的好处:减少单库单表的数据量,提高数据性能。
3.5. 分库分表应用和问题
1.应用
(1)在数据库设计时候考虑垂直分库和垂直分表(垂直切分改变结构,水平切分改变数据量)
(2)随着数据库数据量增加,不要马上考虑做水平切分,首先考虑缓存处理,读写分离,使
用索引等等方式,如果这些方式不能根本解决问题了,再考虑做水平分库和水平分表
2.分库分表问题
(1)跨节点(查询的信息包含课程信息和订单信息,只查一张表是不满足条件,课程和订单信息在不同的服务器不同数据库上,需要先去订单库查询订单信息,再去课程库查询课程信息,然后进行拼接)连接查询问题(分页、排序)
(2)多数据源管理问题(多个数据库,在多个服务器上,在进行操作时,需要管理多个数据源)
4. Sharding-JDBC
定位为轻量级 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 标准的数据库。
归纳重点:
是轻量级的 java 框架,是增强版的 JDBC 驱动
好处:不是做分库分表,是简化对分库分表之后数据相关操作
主要做的两个功能:数据分片和读写分离
5. Sharding-JDBC 实现水平分表
5.1. 环境的搭建
基本环境:SpringBoot 2.2.1+ MyBatisPlus + Sharding-JDBC + Druid 连接池
1.创建 SpringBoot 工程,选择Spring Initializr,点击next
2.填写Group、Artifact,选择Java Version,点击next
3.先选择一个版本,后面需要在pom文件中修改版本号 ,然后点击next
4.点击finish,项目创建成功
5.修改spring boot 版本号,修改版本号为2.2.1.RELEASE
6.配置相关依赖
<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>
<!--druid连接池依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.20</version>
</dependency>
<!--Mysql依赖,SpringBoot用的是2.2.1,对应MySQL是8版本-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--shardingsphere和JDBC 整合的依赖-->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
<!--MyBatis Plus 依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
<!--lombok简化实体类开发,不需要写getter和Setter方法,用注解快速生成-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
可能会出现下面问题:
Project ‘org.springframework.boot:spring-boot-starter-parent:2.2.1.RELEASE‘ not found 报红
原因是idea软件中保存有缓存,所以导致导入springboot依赖时候,明明导入以来成功,却还是报红(not found)
解决方案:在File 》Invalidate C…
7.创建数据库和表
按照水平分表的方式,创建数据库和数据库表
(1)创建数据库 course_db
(2)在数据库创建两张表 course_1 和 course_2
(3)约定规则:如果添加课程 id 是偶数把数据添加 course_1,如果奇数添加到 course_2
--创建course_db库
CREATE DATABASE IF NOT EXISTS `course_db` DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
--创建course_1和course_2表
DROP TABLE IF EXISTS `course_1`;
CREATE TABLE `course_1` (
`cid` BIGINT(20) PRIMARY KEY COMMENT '课程编号',
`cname` VARCHAR(50) NOT NULL COMMENT '课程名称',
`user_id` VARCHAR(20) NOT NULL COMMENT '用户ID',
`cstatus` VARCHAR(10) NOT NULL COMMENT '课程状态'
);
DROP TABLE IF EXISTS `course_2`;
CREATE TABLE `course_2` (
`cid` BIGINT(20) PRIMARY KEY COMMENT '课程编号',
`cname` VARCHAR(50) NOT NULL COMMENT '课程名称',
`user_id` VARCHAR(20) NOT NULL COMMENT '用户ID',
`cstatus` VARCHAR(10) NOT NULL COMMENT '课程状态'
);
8.创建实体类Course
package com.fang.shardingjdbcdemo.entity;
import lombok.Data;
//使用lombok 注解可以不用写setter和getter方法
@Data
public class Course {
private Long cid;
private String cname;
private Long userId;
private String cstatus;
}
9.创建Mapper接口
package com.fang.shardingjdbcdemo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fang.shardingjdbcdemo.entity.Course;
import org.springframework.stereotype.Repository;
//继承BaseMapper,加上实体类,这样可以直接调用MP(MyBatis Plus)中封装的方法
//加上Repository交给Spring来管理
@Repository
public interface CourseMapper extends BaseMapper<Course> {
}
10.配置主启动类
package com.fang.shardingjdbcdemo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
//扫描Mapper
@MapperScan("com.fang.shardingjdbcdemo.mapper")
public class ShardingjdbcdemoApplication {
public static void main(String[] args) {
SpringApplication.run(ShardingjdbcdemoApplication.class, args);
}
}
11.配置application
配置可参考 官网->用户手册->ShardingSphere-JDBC->配置手册->Spring Boot配置
# 数据源名称,多数据源以逗号分隔,可以随便起
spring.shardingsphere.datasource.names=m1
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true
# 配置数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
#若使用MySQL8版本,驱动用com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.driver-class-name=com.mysql.cj.jdbc.Driver
#MySQL8配置地址时,后面加个参数serverTimezone-当前时区,GMT是时区,%2B是转义字符,8代表中国当前时区
#jdbc:mysql://localhost:3306/course_db?serverTimezone=GMT%2B8
#spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/course_db
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/course_db?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=123456
# 配置 t_order 表规则
#指定course表分布情况,配置表在哪个数据库里面,表名称都是什么,ds$->{0..1}是分库,这里没有用m1代替
#m1.course_1,m1.course_2
spring.shardingsphere.sharding.tables.course.actual-data-nodes=m1.course_$->{1..2}
# 分布式序列策略配置,指定course表中主键cid 生成策略
# 分布式序列列名称
spring.shardingsphere.sharding.tables.course.key-generator.column=cid
# 分布式序列算法名称,SNOWFLAKE雪花算法,生成 随机的 唯一的 数字
spring.shardingsphere.sharding.tables.course.key-generator.type=SNOWFLAKE
# 指定分片策略,如果是偶数往course_1中加数据,如果是奇数往course_2表中加数据
# 分片列名称
spring.shardingsphere.sharding.tables.course.table-strategy.inline.sharding-column=cid
# 自动分片算法名称,course_$->{cid % 2},如果cid是偶数往course_0中加数据,不符合上面要求所以course_$->{cid % 2 + 1}
spring.shardingsphere.sharding.tables.course.table-strategy.inline.algorithm-expression=course_$->{cid % 2 + 1}
# 打开sql输出日志
spring.shardingsphere.props.sql.show=true
12.测试类
package com.fang.shardingjdbcdemo;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.fang.shardingjdbcdemo.entity.Course;
import com.fang.shardingjdbcdemo.mapper.CourseMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class ShardingjdbcdemoApplicationTests {
//注入mapper
@Autowired
private CourseMapper courseMapper;
@Test
void contextLoads() {
}
//添加课程的方法
@Test
public void addCourse() {
/*//for(int i=1;i<=10;i++) {
Course course = new Course();
course.setCname("java"+i);
course.setUserId(100L);
course.setCstatus("Normal"+i);
courseMapper.insert(course);
//}*/
Course course = new Course();
course.setCname("java");
course.setUserId(100L);
course.setCstatus("Normal");
courseMapper.insert(course);
}
//查询课程的方法
@Test
public void findCourse() {
//mp中的条件构造器
QueryWrapper<Course> wrapper = new QueryWrapper<>();
//测试奇数
wrapper.eq("cid",652223643794800641L);
Course course = courseMapper.selectOne(wrapper);
System.out.println(course);
}
}
5.2. 测试验证
1.执行ShardingjdbcdemoApplicationTests.addCourse方法
可能会出现以下错误:
The bean 'dataSource', defined in class path resource [org/apache/shardingsphere/shardingjdbc/spring/boot/SpringBootConfiguration.class], could not be registered. A bean with that name has already been defined in class path resource [com/alibaba/druid/spring/boot/autoconfigure/DruidDataSourceAutoConfigure.class] and overriding is disabled.
问题原因是一个实体类不能对应两张表。
解决方案:
在配置文件pom中添加一行配置
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true
2.再次执行,cid是652220080507060225 ,是奇数,%2 + 1为course_2
3.查看course_1和course_2表,发现course_2有数据,说明水平分表成功
4.测试多次插入,查看course_1和course_2数据
先删除上面往数据库中插入的数据
@Test
public void addCourse() {
for(int i=1;i<=10;i++) {
Course course = new Course();
course.setCname("java"+i);
course.setUserId(100L);
course.setCstatus("Normal"+i);
courseMapper.insert(course);
}
/*Course course = new Course();
course.setCname("java");
course.setUserId(100L);
course.setCstatus("Normal");
courseMapper.insert(course);*/
}
5.查看course_1和course_2表数据,发现cid为偶数插入course_1中,为奇数course_2中
6.测试查询
执行ShardingjdbcdemoApplicationTests.findCourse(),发现查询course_2表
6. Sharding-JDBC 实现水平分库
6.1. 环境搭建
1.需求分析
创建edu_db_1和edu_db_2库,每个库在分表创建course_1和course_2
分片规则:
数据库规则:userid为偶数,数据添加到edu_db_1库中,userid为奇数,数据添加到edu_db_2库中
表规则:cid为偶数,数据添加到course_1表中,cid为奇数,数据添加到course_2表中
2.创建数据库和表
--创建edu_db_1库,和该库下的course_1和course_2
CREATE DATABASE IF NOT EXISTS `edu_db_1` DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
DROP TABLE IF EXISTS `course_1`;
CREATE TABLE `course_1` (
`cid` BIGINT(20) PRIMARY KEY COMMENT '课程编号',
`cname` VARCHAR(50) NOT NULL COMMENT '课程名称',
`user_id` VARCHAR(20) NOT NULL COMMENT '用户ID',
`cstatus` VARCHAR(10) NOT NULL COMMENT '课程状态'
);
DROP TABLE IF EXISTS `course_2`;
CREATE TABLE `course_2` (
`cid` BIGINT(20) PRIMARY KEY COMMENT '课程编号',
`cname` VARCHAR(50) NOT NULL COMMENT '课程名称',
`user_id` VARCHAR(20) NOT NULL COMMENT '用户ID',
`cstatus` VARCHAR(10) NOT NULL COMMENT '课程状态'
);
--创建edu_db_2库,和该库下的course_1和course_2
CREATE DATABASE IF NOT EXISTS `edu_db_2` DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
DROP TABLE IF EXISTS `course_1`;
CREATE TABLE `course_1` (
`cid` BIGINT(20) PRIMARY KEY COMMENT '课程编号',
`cname` VARCHAR(50) NOT NULL COMMENT '课程名称',
`user_id` VARCHAR(20) NOT NULL COMMENT '用户ID',
`cstatus` VARCHAR(10) NOT NULL COMMENT '课程状态'
);
DROP TABLE IF EXISTS `course_2`;
CREATE TABLE `course_2` (
`cid` BIGINT(20) PRIMARY KEY COMMENT '课程编号',
`cname` VARCHAR(50) NOT NULL COMMENT '课程名称',
`user_id` VARCHAR(20) NOT NULL COMMENT '用户ID',
`cstatus` VARCHAR(10) NOT NULL COMMENT '课程状态'
);
3.配置application
# 数据源名称,多数据源以逗号分隔,可以随便起
# shardingjdbc 分片策略,水平分库,配置两个数据源
spring.shardingsphere.datasource.names=m1,m2
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true
#配置第一个数据源具体内容,包含连接池,驱动,地址,用户名和密码
# 配置数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
#若使用MySQL8版本,驱动用com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.driver-class-name=com.mysql.cj.jdbc.Driver
#MySQL8配置地址时,后面加个参数serverTimezone-当前时区,GMT是时区,%2B是转义字符,8代表中国当前时区
#jdbc:mysql://localhost:3306/course_db?serverTimezone=GMT%2B8
#spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/course_db
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/edu_db_1?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=123456
#配置第二个数据源具体内容,包含连接池,驱动,地址,用户名和密码
# 配置数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource
#若使用MySQL8版本,驱动用com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m2.driver-class-name=com.mysql.cj.jdbc.Driver
#MySQL8配置地址时,后面加个参数serverTimezone-当前时区,GMT是时区,%2B是转义字符,8代表中国当前时区
#jdbc:mysql://localhost:3306/course_db?serverTimezone=GMT%2B8
#spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/course_db
spring.shardingsphere.datasource.m2.url=jdbc:mysql://localhost:3306/edu_db_2?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=123456
#指定course表分布情况,配置表在哪个数据库里面,表名称都是什么,ds$->{0..1}是分库,这里没有用m1代替
# m1 m2 course_1 course_2
spring.shardingsphere.sharding.tables.course.actual-data-nodes=m$->{1..2}.course_$->{1..2}
# 分布式序列策略配置,指定course表中主键cid 生成策略
# 分布式序列列名称
spring.shardingsphere.sharding.tables.course.key-generator.column=cid
# 分布式序列算法名称,SNOWFLAKE雪花算法,生成 随机的 唯一的 数字
spring.shardingsphere.sharding.tables.course.key-generator.type=SNOWFLAKE
# 指定表分片策略,如果是偶数往course_1中加数据,如果是奇数往course_2表中加数据
# 分片列名称
spring.shardingsphere.sharding.tables.course.table-strategy.inline.sharding-column=cid
# 自动分片算法名称,course_$->{cid % 2},如果cid是偶数往course_0中加数据,不符合上面要求所以course_$->{cid % 2 + 1}
spring.shardingsphere.sharding.tables.course.table-strategy.inline.algorithm-expression=course_$->{cid % 2 + 1}
# 指定数据库分片策略 约定 user_id 是偶数添加 m1,是奇数添加 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
4.编写测试方法
ShardingjdbcdemoApplicationTests中添加下面方法
//======================测试水平分库=====================
//添加操作
@Test
public void addCourseDb() {
Course course = new Course();
course.setCname("javademo1");
//分库根据 user_id
course.setUserId(100L);
course.setCstatus("Normal1");
courseMapper.insert(course);
}
//查询操作
@Test
public void findCourseDb() {
QueryWrapper<Course> wrapper = new QueryWrapper<>();
//设置 userid 值
wrapper.eq("user_id",100L);
//设置 cid 值
wrapper.eq("cid",465162909769531393L);
Course course = courseMapper.selectOne(wrapper);
System.out.println(course);
}
5.测试结果
执行addCourseDb方法,UserId为100,cid为652252376081629185,根据分片策略,会往edu_db_1中插入数据,cid为奇数会往course_2插入数据
查询 edu_db_1、edu_db_2中course_1和course_2,发现只有edu_db_1中的course_2表有数据,测试结果符合预期。
执行findCourseDb方法,serId为101,cid为652252376081629186,根据分片策略,会在edu_db_2中,查询course_1数据,测试结果符合预期。
7. Sharding-JDBC 实现垂直分库
7.1. 环境搭建
1.需求分析
有user_db、course_db、order_db三张表,查询用户信息,只去user_db中t_user表查询,当添加用户信息时,只去user_db中t_user表查询。实现专库专表。
2.创建user_db库和t_user表
--创建user_db库和表t_user
CREATE DATABASE IF NOT EXISTS `user_db` DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
`user_id` BIGINT(20) PRIMARY KEY COMMENT '用户编号',
`username` VARCHAR(100) NOT NULL COMMENT '用户名称',
`ustatus` VARCHAR(50) NOT NULL COMMENT '用户状态'
);
3.创建实体类User
package com.fang.shardingjdbcdemo.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
//使用lombok 注解可以不用写setter和getter方法
@Data
@TableName(value = "t_user") //指定对应表
public class User {
private Long userId;
private String username;
private String ustatus;
}
4.创建Mapper接口
package com.fang.shardingjdbcdemo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fang.shardingjdbcdemo.entity.User;
import org.springframework.stereotype.Repository;
//继承BaseMapper,加上实体类,这样可以直接调用MP(MyBatis Plus)中封装的方法
//加上Repository交给Spring来管理
@Repository
public interface UserMapper extends BaseMapper<User> {
}
5.配置application
# 数据源名称,多数据源以逗号分隔,可以随便起
# shardingjdbc 分片策略,水平分库,配置两个数据源
spring.shardingsphere.datasource.names=m1,m2,m0
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true
#配置第一个数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
#若使用MySQL8版本,驱动用com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.driver-class-name=com.mysql.cj.jdbc.Driver
#MySQL8配置地址时,后面加个参数serverTimezone-当前时区,GMT是时区,%2B是转义字符,8代表中国当前时区
#jdbc:mysql://localhost:3306/course_db?serverTimezone=GMT%2B8
#spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/course_db
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/edu_db_1?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=123456
#配置第二个数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource
#若使用MySQL8版本,驱动用com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m2.driver-class-name=com.mysql.cj.jdbc.Driver
#MySQL8配置地址时,后面加个参数serverTimezone-当前时区,GMT是时区,%2B是转义字符,8代表中国当前时区
#jdbc:mysql://localhost:3306/course_db?serverTimezone=GMT%2B8
#spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/course_db
spring.shardingsphere.datasource.m2.url=jdbc:mysql://localhost:3306/edu_db_2?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=123456
#配置m0数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m0.type=com.alibaba.druid.pool.DruidDataSource
#若使用MySQL8版本,驱动用com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m0.driver-class-name=com.mysql.cj.jdbc.Driver
#MySQL8配置地址时,后面加个参数serverTimezone-当前时区,GMT是时区,%2B是转义字符,8代表中国当前时区
#jdbc:mysql://localhost:3306/course_db?serverTimezone=GMT%2B8
#spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/course_db
spring.shardingsphere.datasource.m0.url=jdbc:mysql://localhost:3306/user_db?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=123456
# 配置user_db数据库里面t_user 专库专表
spring.shardingsphere.sharding.tables.t_user.actual-data-nodes=m$->{0}.t_user
# 分布式序列列名称,分布式序列策略配置,指定t_user表中主键user_id 生成策略
spring.shardingsphere.sharding.tables.t_user.key-generator.column=user_id
# 分布式序列算法名称,SNOWFLAKE雪花算法,生成 随机的 唯一的 数字
spring.shardingsphere.sharding.tables.t_user.key-generator.type=SNOWFLAKE
# 指定表分片策略,这里么有水平分表,直接填写t_user
# 分片列名称
spring.shardingsphere.sharding.tables.course.table-strategy.inline.sharding-column=user_id
# 自动分片算法名称,course_$->{cid % 2},如果cid是偶数往course_0中加数据,不符合上面要求所以course_$->{cid % 2 + 1}
spring.shardingsphere.sharding.tables.course.table-strategy.inline.algorithm-expression=t_user
# 指定数据库分片策略 约定 user_id 是偶数添加 m1,是奇数添加 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
6.编写测试代码
ShardingjdbcdemoApplicationTests中添加下面代码
//注入 user 的 mapper
@Autowired
private UserMapper userMapper;
添加下面方法
//======================测试垂直分库==================
//添加操作
@Test
public void addUserDb() {
User user = new User();
user.setUsername("lucy");
user.setUstatus("a");
userMapper.insert(user);
}
//查询操作
@Test
public void findUserDb() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
//设置 userid 值
wrapper.eq("user_id",652270380941049857L);
User user = userMapper.selectOne(wrapper);
System.out.println(user);
}
7.执行测试方法
执行addUserDb,可能会出现下面报错
java.lang.IllegalStateException: Missing the data source name: 'null'
问题时,user_db库中 user表命名为t_user,而不是user或者user_1等等,进行数据库操作找不到对应的表。
解决方案:User实体类指定对应的表,
@TableName(value = "t_user") //指定对应表
测试结果,发现插入的user_db中的t_user表,做到了专库专表
执行findUserDb,发现去user_db中去查t_user表,做到专库专表
8. Sharding-JDBC 操作公共表
8.1. 什么是公共表
1.存储固定数据的表,表数据很少发生变化,查询时候经常进行关联
2.在每个数据库中创建出相同结构公共表
8.2. 环境准备
1.在多个数据库都创建相同结构公共表
分别在在user_db、edu_db_1、edu_db_2 中创建公共表
DROP TABLE IF EXISTS `t_udict`;
CREATE TABLE `t_udict` (
`dictid` BIGINT(20) PRIMARY KEY COMMENT '字典编号',
`ustatus` VARCHAR(100) NOT NULL COMMENT '状态码',
`uvalue` VARCHAR(100) NOT NULL COMMENT '状态名称'
);
2.配置application
# 数据源名称,多数据源以逗号分隔,可以随便起
# shardingjdbc 分片策略,水平分库,配置两个数据源
spring.shardingsphere.datasource.names=m1,m2,m0
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true
#配置第一个数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
#若使用MySQL8版本,驱动用com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.driver-class-name=com.mysql.cj.jdbc.Driver
#MySQL8配置地址时,后面加个参数serverTimezone-当前时区,GMT是时区,%2B是转义字符,8代表中国当前时区
#jdbc:mysql://localhost:3306/course_db?serverTimezone=GMT%2B8
#spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/course_db
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/edu_db_1?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=123456
#配置第二个数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource
#若使用MySQL8版本,驱动用com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m2.driver-class-name=com.mysql.cj.jdbc.Driver
#MySQL8配置地址时,后面加个参数serverTimezone-当前时区,GMT是时区,%2B是转义字符,8代表中国当前时区
#jdbc:mysql://localhost:3306/course_db?serverTimezone=GMT%2B8
#spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/course_db
spring.shardingsphere.datasource.m2.url=jdbc:mysql://localhost:3306/edu_db_2?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=123456
#配置m0数据源具体内容,包含连接池,驱动,地址,用户名和密码
spring.shardingsphere.datasource.m0.type=com.alibaba.druid.pool.DruidDataSource
#若使用MySQL8版本,驱动用com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m0.driver-class-name=com.mysql.cj.jdbc.Driver
#MySQL8配置地址时,后面加个参数serverTimezone-当前时区,GMT是时区,%2B是转义字符,8代表中国当前时区
#jdbc:mysql://localhost:3306/course_db?serverTimezone=GMT%2B8
#spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/course_db
spring.shardingsphere.datasource.m0.url=jdbc:mysql://localhost:3306/user_db?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=123456
# 配置公共表
spring.shardingsphere.sharding.broadcast-tables=t_udict
spring.shardingsphere.sharding.tables.t_udict.key-generator.column=dictid
# 分布式序列算法名称,SNOWFLAKE雪花算法,生成 随机的 唯一的 数字
spring.shardingsphere.sharding.tables.t_udict.key-generator.type=SNOWFLAKE
# 配置user_db数据库里面t_user 专库专表
spring.shardingsphere.sharding.tables.t_user.actual-data-nodes=m$->{0}.t_user
# 分布式序列列名称,分布式序列策略配置,指定t_user表中主键user_id 生成策略
spring.shardingsphere.sharding.tables.t_user.key-generator.column=user_id
# 分布式序列算法名称,SNOWFLAKE雪花算法,生成 随机的 唯一的 数字
spring.shardingsphere.sharding.tables.t_user.key-generator.type=SNOWFLAKE
# 指定表分片策略,这里么有水平分表,直接填写t_user
# 分片列名称
spring.shardingsphere.sharding.tables.course.table-strategy.inline.sharding-column=user_id
# 自动分片算法名称,course_$->{cid % 2},如果cid是偶数往course_0中加数据,不符合上面要求所以course_$->{cid % 2 + 1}
spring.shardingsphere.sharding.tables.course.table-strategy.inline.algorithm-expression=t_user
# 指定数据库分片策略 约定 user_id 是偶数添加 m1,是奇数添加 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
3.创建实体类Udict
package com.fang.shardingjdbcdemo.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
//使用lombok 注解可以不用写setter和getter方法
@Data
@TableName(value = "t_udict")//指定对应表
public class Udict {
private Long dictid;
private String ustatus;
private String uvalue; }
4.创建Mapper接口
package com.fang.shardingjdbcdemo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.fang.shardingjdbcdemo.entity.Udict;
import org.springframework.stereotype.Repository;
//继承BaseMapper,加上实体类,这样可以直接调用MP(MyBatis Plus)中封装的方法
//加上Repository交给Spring来管理
@Repository
public interface UdictMapper extends BaseMapper<Udict> {
}
5.编写测试方法
@Autowired
private UdictMapper udictMapper;
//======================测试公共表===================
//添加操作
@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",652281395384680449L);
udictMapper.delete(wrapper);
}
6.执行addDict方法
进行添加操作时,三个数据库中t_udict 都有数据
7.执行deleteDict方法
进行删除操作时,三个数据库中t_udict 中的652281395384680449L数据都删除掉了
9. Sharding-JDBC 实现读写分离
9.1.什么是读写分离
读写分离是怎么实现的?它的原理是怎样的?
读写分离也是针对数据量比较大,提高数据库查询效率的一种方案。
读写分离的原理:
Sharding-JDBC 通过 sql 语句语义分析,实现读写分离过程,不会做数据同步。
9.2. MySQL配置读写分离
创建两个 MySQL 数据库服务,并且启动两个 MySQL 服务。
1.复制之前 MySQL 目录,重命名为mysql-8.0.25-winx64-s,表示从库(slave)
2.修改mysql-8.0.25-winx64-s下的配置文件my.ini
修改port、basedir、basedir,port从3306修改为3307,basedir和datadir修改为从库的位置
3.删除 D:\CodeSoft\MySQL\mysql-8.0.25-winx64-s\data 目录下的auto.cnf
因为 mysql-8.0.25-winx64 和 mysql-8.0.25-winx64-s 中的 auto.cnf UUID重复会导致Error_code: MY-013117错误,删了重启会重新生成。
4.安装从库在window的服务
在 D:\CodeSoft\MySQL\mysql-8.0.25-winx64-s\bin 路径下输入cmd,在cmd页面输入下面命令mysqld --install mysqls1 --defaults-file="D:\CodeSoft\MySQL\mysql-8.0.25-winx64-s\bin\my.ini"
5.执行命令后发现从库服务已安装成功
6.启动从库服务
在 D:\CodeSoft\MySQL\mysql-8.0.25-winx64-s\bin 路径下输入cmd,在cmd页面输入下面命令
使用 net start mysqls1 命令启动mysql服务。若启动不成功可以通过命令删除该服务
#添加服务命令
mysqld --install mysqls1 --defaults-file="D:\CodeSoft\MySQL\mysql-8.0.25-winx64-s\bin\my.ini"
#启动服务命令
net start mysqls1
#删除服务命令
sc delete mysqls1
由于从库是从主库复制过来的,因此里面的数据完全一致,可使用原来的账号、密码登录。
7.配置MySQL主服务器,在 D:\CodeSoft\MySQL\mysql-8.0.25-winx64\bin 目录下找到my.ini,配置下面内容
[mysqld]
#开启日志
log-bin = mysql-bin
#设置服务id,主从不能一致
server-id = 1
#设置需要同步的数据库
binlog-do-db=user_db
#屏蔽系统库同步
binlog-ignore-db=mysql
binlog-ignore-db=information_schema
binlog-ignore-db=performance_schema
8.配置MySQL从服务器
在 D:\CodeSoft\MySQL\mysql-8.0.25-winx64-s\bin 目录下找到my.ini,配置下面内容
[mysqld]
#开启日志
log-bin = mysql-bin
#设置服务id,主从不能一致
server-id = 2
#设置需要同步的数据库
replicate_wild_do_table=user_db.%
#屏蔽系统库同步
replicate_wild_ignore_table=mysql.%
replicate_wild_ignore_table=information_schema.%
replicate_wild_ignore_table=performance_schema.%
9.重启主从MySQL,如果没有报错,说明上面配置没有问题
10.创建用于主从复制的账号,账号为db_sync
#切换至主库bin目录,登录主库
mysql -h localhost -uroot -p123456;
#授权主备复制专用账号
#MySQL5 可采用 GRANT REPLICATION SLAVE ON *.* TO 'db_sync'@'%' IDENTIFIED BY 'db_sync';
#创建db_sync用户和密码
CREATE USER 'db_sync'@'%' IDENTIFIED BY 'db_sync';
GRANT REPLICATION SLAVE ON *.* TO 'db_sync'@'%';
#刷新权限
FLUSH PRIVILEGES;
#确认位点 记录下文件名以及位点
show master status;
show master status; 命令显示 binlog日志名和binlog日志中位置,文件名和位置后面配置从服务器会用到
9.设置从库向主库同步数据
#切换至从库bin目录,登录从库
mysql -hlocalhost -P3307 -uroot -p123456
#先停止同步
STOP SLAVE;
#修改从库指向到主库,使用上一步记录的文件名以及位点
CHANGE MASTER TO
master_host = 'localhost',
master_user = 'db_sync',
master_password = 'db_sync',
master_log_file = 'binlog.000006',
master_log_pos = 1175;
#启动同步
START SLAVE;
10.查看从库状态Slave_IO_Runing和Slave_SQL_Runing都为Yes说明同步成功,如果不为Yes,请检查error_log,然后排查相关异常。
show slave status;
注意 如果之前此从库已有主库指向 需要先执行以下命令清空
STOP SLAVE IO_THREAD FOR CHANNEL '';
reset slave all;
11.测试主从复制,修改或者添加主服务器中user_db库中t_user表中数据,检查从服务器中user_db库中t_user表中数据,发现数据被通过过来了
9.3.使用Sharding-JDBC进行操作
Sharding-JDBC并不做主从复制,主从复制是由MySQL做的,Sharding-JDBC根据语义,当做查询,它会到从服务器中,当做增加、修改、删除,它会到主服务器中
1.配置application,增加数据源s0配置等信息
# shardingjdbc分片策略
# 配置数据源,给数据源起名称,
# 水平分库,配置两个数据源
spring.shardingsphere.datasource.names=m1,m2,m0,s0
# 一个实体类对应两张表,覆盖
spring.main.allow-bean-definition-overriding=true
#配置第一个数据源具体内容,包含连接池,驱动,地址,用户名和密码
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
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
spring.shardingsphere.datasource.m2.username=root
spring.shardingsphere.datasource.m2.password=123456
#配置第三个数据源具体内容,包含连接池,驱动,地址,用户名和密码
# user_db主服务器
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
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=123456
# user_db从服务器
spring.shardingsphere.datasource.s0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.s0.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.s0.url=jdbc:mysql://localhost:3307/user_db?serverTimezone=GMT%2B8
spring.shardingsphere.datasource.s0.username=root
spring.shardingsphere.datasource.s0.password=123456
# 主库从库逻辑数据源定义 ds0为user_db
spring.shardingsphere.sharding.master-slave-rules.ds0.master-data-source-name=m0
spring.shardingsphere.sharding.master-slave-rules.ds0.slave-data-source-names=s0
# 配置user_db数据库里面t_user 专库专表
#spring.shardingsphere.sharding.tables.t_user.actual-data-nodes=m$->{0}.t_user
# t_user分表策略,固定分配至ds0的t_user真实表
spring.shardingsphere.sharding.tables.t_user.actual-data-nodes=ds0.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
# 指定course表里面主键cid 生成策略 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
#指定数据库分布情况,数据库里面表分布情况
# 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,是奇数添加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
2.编写测试代码
ShardingjdbcdemoApplicationTests中添加下面方法
//添加操作
@Test
public void addUser2Db() {
User user = new User();
user.setUsername("lucymary");
user.setUstatus("a");
userMapper.insert(user);
}
//查询操作
@Test
public void findUser2Db() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
//设置 userid 值
wrapper.eq("user_id",652270380941049858L);
User user = userMapper.selectOne(wrapper);
System.out.println(user);
}
3. 测试结果
执行addUser2Db添加方法,查看主库是否已经添加,从库是否从主库同步最新添加的数据
执行findUser2Db添加方法,去从库查询数据,符合预期
4.一主多从配置
10. Sharding-Proxy
10.1. 什么是Sharding-Proxy
定位为透明化的数据库代理端,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前提供 MySQL 和 PostgreSQL 版本,它可以使用任何兼容 MySQL/PostgreSQL 协议的访问客户端(如:MySQL Command Client, MySQL Workbench, Navicat 等)操作数据,对 DBA 更加友好。
- 向应用程序完全透明,可直接当做 MySQL/PostgreSQL 使用。
- 适用于任何兼容 MySQL/PostgreSQL 协议的的客户端。
归纳重点:
定位为透明的数据库代理端(以前分库分表、读写分离,需要在配置文件中配置多个数据源和分库分表规则,现在用Sharding-Proxy后,这些都是Proxy在处理,不用配置多个数据源和分库分表规则)
10.2.下载安装
Sharding-Proxy 独立应用,需要安装服务,进行分库分表或者读写分离配置,启动使用
1.可在官网上进行下载
2.把下载之后压缩文件,解压,启动 bin 目录启动文件就可以了,这里下载的是apache-shardingsphere-4.1.0-sharding-proxy-bin.tar.gz,conf目录下
注意:可能会出现解压后lib中的文件后缀名不是jar,需要手动修改,如果不改会出现类找不到问题
10.3. Sharding-Proxy 配置
1.修改conf下面的server.yaml,添加下面内容,sharding_db 为sharding-proxy里面的库名。
也可在Idea中新建一个yml文件把server.yaml里面注释的内容复制进去,通过Ctrl+/ 去掉注释,然后在贴到server.yaml 中。
2.修改config-sharding.yaml
authentication:
users:
root:
password: 123456
sharding:
password: sharding
authorizedSchemas: sharding_db
props:
max.connections.size.per.query: 1
acceptor.size: 16 # The default value is available processors count * 2.
executor.size: 16 # Infinite by default.
proxy.frontend.flush.threshold: 128 # The default value is 128.
# LOCAL: Proxy will run with LOCAL transaction.
# XA: Proxy will run with XA transaction.
# BASE: Proxy will run with B.A.S.E transaction.
proxy.transaction.type: LOCAL
proxy.opentracing.enabled: false
proxy.hint.enabled: false
query.with.cipher.column: true
sql.show: false
allow.range.query.with.inline.sharding: false
3.打开config-sharding.yaml 文件
连接mysql,需要复制 mysql 驱动 jar 包到 lib 目录,去maven仓库找jar包
4.配置分库分表规则
打开 config-sharding.yaml 文件,添加下面内容
schemaName: sharding_db
dataSources:
ds_0:
url: jdbc:mysql://127.0.0.1:3306/edu_1?serverTimezone=UTC&useSSL=false
username: root
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
shardingRule:
tables:
t_order:
actualDataNodes: ds_${0}.t_order_${0..1}
tableStrategy:
inline:
shardingColumn: order_id
algorithmExpression: t_order_${order_id % 2}
keyGenerator:
type: SNOWFLAKE
column: order_id
bindingTables:
- t_order
defaultDatabaseStrategy:
inline:
shardingColumn: user_id
algorithmExpression: ds_${0}
defaultTableStrategy:
none:
5.主库上创建edu_1,不用创建表,shardingProxy一连接库时,自动创建表
--创建edu_1库
CREATE DATABASE IF NOT EXISTS `edu_1` DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
5.启动,进入bin目录,Sharding-Proxy 默认端口号 3307,双击start.bat,会出现端口已被占用的错,需要启动是 传入 3308
start.bat 3308
已经启动成功
6.通过 navicat 连接 Sharding-Proxy
连接方式和连接 mysql 一样的,输入用户名和密码,分别为root和123456
7.插入信息
--创建t_order表
create table if not exists ds_0.t_order(
order_id bigint not null,
user_id int not null,
status varchar(50),
primary key(order_id)
);
--使用sharding_db库
USE sharding_db;
--查看sharding_db库下的表
show tables;
--查看订单表信息
select * from t_order;
--往订单表插入信息
insert into t_order(order_id,user_id,status) values (11,1,'init');
--查看订单表信息
select * from t_order;
8.发现3306端口下面edu_1库下面已经创建好t_order_0和t_order_1,并且分表规则往t_order_1插入了一条数据
10.4. Sharding-Proxy 配置(分库)
1.使用edu_db_1和edu_db_2库
2.配置 config-sharding.yaml
进入conf目录下面配置 config-sharding.yaml,配置下面
schemaName: sharding_db
dataSources:
ds_0:
url: jdbc:mysql://127.0.0.1:3306/edu_db_1?serverTimezone=UTC&useSSL=false
username: root
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
ds_1:
url: jdbc:mysql://127.0.0.1:3306/edu_db_1?serverTimezone=UTC&useSSL=false
username: root
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
shardingRule:
tables:
t_order:
actualDataNodes: ds_${0..1}.t_order_${1..2}
tableStrategy:
inline:
shardingColumn: order_id
algorithmExpression: t_order_${order_id % 2 + 1}
keyGenerator:
type: SNOWFLAKE
column: order_id
bindingTables:
- t_order
defaultDatabaseStrategy:
inline:
shardingColumn: user_id
algorithmExpression: ds_${user_id % 2}
defaultTableStrategy:
none:
3.启动 Sharding-Proxy 服务
进入 apache-shardingsphere-4.1.0-sharding-proxy-bin\bin 下面进入cmd 命令行页面,输入start.bat 3308
4.打开 cmd 仓库,连接 Sharding-Proxy 服务
5.执行下面命令
--创建t_order表
create table if not exists ds_0.t_order(
order_id bigint not null,
user_id int not null,
status varchar(50),
primary key(order_id)
);
--使用sharding_db库
USE sharding_db;
--查看sharding_db库下的表
show tables;
--查看订单表信息
select * from t_order;
--往订单表插入信息
insert into t_order(order_id,user_id,status) values (11,1,'init');
--查看订单表信息
select * from t_order;
6.刷新库表,发现edu_db_1和edu_db_2 已经有了t_order_1和t_order_2
7.检查数据
插入数据 user_id 为1,是奇数,插入edu_db_2中。order_id 为11,是奇数,插入t_order_2中,发现符合预期
10.5. Sharding-Proxy 配置(读写分离)
1.在3306上,创建三个库 demo_ds_master、demo_ds_slave_0、demo_ds_slave_1
CREATE DATABASE IF NOT EXISTS `demo_ds_master` DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
CREATE DATABASE IF NOT EXISTS `demo_ds_slave_0` DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
CREATE DATABASE IF NOT EXISTS `demo_ds_slave_1` DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
2.修改 conf 里面配置文件,config-master-slave.yaml
schemaName: master_slave_db
dataSources:
master_ds:
url: jdbc:mysql://127.0.0.1:3306/demo_ds_master?serverTimezone=UTC&useSSL=false
username: root
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
slave_ds_0:
url: jdbc:mysql://127.0.0.1:3306/demo_ds_slave_0?serverTimezone=UTC&useSSL=false
username: root
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
slave_ds_1:
url: jdbc:mysql://127.0.0.1:3306/demo_ds_slave_1?serverTimezone=UTC&useSSL=false
username: root
password: 123456
connectionTimeoutMilliseconds: 30000
idleTimeoutMilliseconds: 60000
maxLifetimeMilliseconds: 1800000
maxPoolSize: 50
masterSlaveRule:
name: ms_ds
masterDataSourceName: master_ds
slaveDataSourceNames:
- slave_ds_0
- slave_ds_1
3.启动 Sharding-Proxy 服务
进入 apache-shardingsphere-4.1.0-sharding-proxy-bin\bin 下面进入cmd 命令行页面,输入start.bat 3308
4.通过 cmd 连接 Sharding-Proxy,进行创建表和添加记录操作
mysql -P3308 -uroot -p123456
show databases;
use master_slave_db;
--在主数据库和从数据库里面,都创建数据库表
create table if not exists demo_ds_master.t_order(
order_id bigint not null,
user_id int not null,
status varchar(50),
primary key(order_id)
);
create table if not exists demo_ds_slave_0.t_order(
order_id bigint not null,
user_id int not null,
status varchar(50),
primary key(order_id)
);
create table if not exists demo_ds_slave_1.t_order(
order_id bigint not null,
user_id int not null,
status varchar(50),
primary key(order_id)
);
5.向表添加记录,不指定向哪个库添加
*把添加数据添加到主数据库里面,两个从库t_order表中没数据
insert into t_order(order_id,user_id,status) values (11,1,'init');
6.查询数据库表数据,不指定查询哪个库
* 直接执行查询从库里面的数据
select * from t_order;
7.从库插入下面数据
insert into demo_ds_slave_0.t_order(order_id,user_id,status) values (100,1,'old');
insert into demo_ds_slave_1.t_order(order_id,user_id,status) values (122,1,'old');
8.进行多次查询,发现查询会随机在从库中查询