一、shardingSphere介绍
1、官网:Apache ShardingSphere
2、开发文档: 概览 :: ShardingSphere
3、shardingsphere-jdbc
ShardingSphere-JDBC 定位为轻量级 Java 框架,在 Java 的 JDBC 层提供的额外服务。 它使用客户端直连数据库,以 jar 包形式提供服务,无需额外部署和依赖,可理解为增强版的 JDBC 驱动,完全兼容 JDBC 和各种 ORM 框架。
官网示例图:
4、shardingSphere-proxy
定位为透明化的数据库代理端
,提供封装了数据库二进制协议的服务端版本,用于完成对异构语言的支持。 目前提供 MySQL 和 PostgreSQL版本,它可以使用任何兼容 MySQL/PostgreSQL 协议的访问客户端(如:MySQL Command Client, MySQL Workbench, Navicat 等)操作数据,对 DBA 更加友好。
官网示例图:
5、两者的区别
二、使用docker安装mysql服务器
1、docker未安装的请看
docker环境安装
注意:如果此时防火墙是开启的,则先关闭防火墙,并重启docker
,否则后续安装的MySQL无法启动(或者在服务器开放对应的端口号,可以提前开启3301,3302,3306,3307,3308,3310,3311,3321)
#关闭docker
systemctl stop docker
#关闭防火墙
systemctl stop firewalld
#启动docker
systemctl start docker
2、在docker中创建并启动MySQL主服务器
第一步:创建并启动mysql
docker run -d \
-p 3306:3306 \
-v /usr/local/docker/mysql/master/conf:/etc/mysql/conf.d \
-v /usr/local/docker/mysql/master/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name lkx-mysql-master \
mysql:8.0.29
第二步:创建MySQL主服务器配置文件
vim /usr/local/docker/mysql/master/conf/my.cnf
将以下配置复制进去并保存
[mysqld]
# 服务器唯一id,默认值1
server-id=1
# 设置日志格式,默认值ROW
binlog_format=STATEMENT
# 二进制日志名,默认binlog
# log-bin=binlog
# 设置需要复制的数据库,默认复制全部数据库
#binlog-do-db=mytestdb
# 设置不需要复制的数据库
#binlog-ignore-db=mysql
#binlog-ignore-db=infomation_schema
binlog格式说明:
-
binlog_format=STATEMENT:日志记录的是主机数据库的
写指令
,性能高,但是now()之类的函数以及获取系统参数的操作会出现主从数据不同步的问题。 -
binlog_format=ROW(默认):日志记录的是主机数据库的
写后的数据
,批量操作时性能较差,解决now()或者 user()或者 @@hostname 等操作在主从机器上不一致的问题。 -
binlog_format=MIXED:是以上两种level的混合使用,有函数用ROW,没函数用STATEMENT,但是无法识别系统变量
第三步: 重启MySQL容器
docker restart lkx-mysql-master
restart:重启
start:启动
stop:停止
第四步:使用命令行登录MySQL主服务器 ,并使root账号在数据库可视化工具可以连接
#进入容器:env LANG=C.UTF-8 避免容器中显示中文乱码
docker exec -it lkx-mysql-master env LANG=C.UTF-8 /bin/bash
#进入容器内的mysql命令行
mysql -uroot -p
#修改默认密码校验方式
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
第五步:主机中创建slave用户
-- 创建slave用户
CREATE USER 'lkx_slave'@'%';
-- 设置密码
ALTER USER 'lkx_slave'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
-- 授予复制权限
GRANT REPLICATION SLAVE ON *.* TO 'lkx_slave'@'%';
-- 刷新权限
FLUSH PRIVILEGES;
第六步:查看主服务器的binlog文件名以及位置号
注意:此操作后不再操作此主mysql服务器,防止主服务器状态值变化
SHOW MASTER STATUS;
此时记录:binlog.0000003 1357两个值
3、在docker中创建并启动两个MySql从服务器
【1】重复执行创建MySql主服务器的,第一步到第四步,按顺序执行两遍。注意映射的端口号与容器名称别一样,这里自定义就行。
我这里举个例子:
docker run -d \
-p 3307:3306 \
-v /usr/local/docker/mysql/slave1/conf:/etc/mysql/conf.d \
-v /usr/local/docker/mysql/slave1/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name lkx-mysql-slave1 \
mysql:8.0.29
docker run -d \
-p 3308:3306 \
-v /usr/local/docker/mysql/slave2/conf:/etc/mysql/conf.d \
-v /usr/local/docker/mysql/slave2/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name lkx-mysql-slave2 \
mysql:8.0.29
【2】在从机上配置主从关系
注意:一定要在从机上操作,并且两台从机都要执行
CHANGE MASTER TO MASTER_HOST='47.97.68.78',
MASTER_USER='lkx_slave',MASTER_PASSWORD='123456', MASTER_PORT=3306,
MASTER_LOG_FILE='binlog.000003',MASTER_LOG_POS=1357;
【3】启动主从同步
-- 在从服务器下查看状态(不需要分号)
SHOW SLAVE STATUS\G
我这边binlog文件名与位置不一样是因为我重启过服务,所以这里你们显示的就是上一步配置的binlog文件名与位置号
两个关键进程:下面两个参数都是Yes,则说明主从配置成功!
可能会出现一下情况,这时候表示从机的IO还没启动好,此时在等等然后再查看。
【4】测试主从同步的情况
在主机中执行以下SQL,在从机中查看数据库、表和数据是否已经被同步。或者直接在可视化工具下操作主MySql服务器,然后看从MySql服务器是否同步
CREATE DATABASE db_user;
USE db_user;
CREATE TABLE t_user (
id BIGINT AUTO_INCREMENT,
uname VARCHAR(30),
PRIMARY KEY (id)
);
INSERT INTO t_user(uname) VALUES('zhang3');
INSERT INTO t_user(uname) VALUES(@@hostname);
三、ShardingSphere-JDBC读写分离
1、创建SpringBoot项目
2、引入maven依赖
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
3、读写分离配置文件
server:
port: 8888
spring:
# 应用名称
application:
name: ShardingSphere-JDBC
# 开发环境设置
profiles:
active: dev
shardingsphere:
datasource:
# 配置真实数据源
names: master,slave1,slave2
master:
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://47.97.68.78:3306/db_user?characterEncoding=utf-8
password: 123456
type: com.zaxxer.hikari.HikariDataSource
username: root
slave1:
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://47.97.68.78:3307/db_user?characterEncoding=utf-8
password: 123456
type: com.zaxxer.hikari.HikariDataSource
username: root
slave2:
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://47.97.68.78:3308/db_user?characterEncoding=utf-8
password: 123456
type: com.zaxxer.hikari.HikariDataSource
username: root
# 内存模式
mode:
type: Memory
# 打印SQl 在控制台查看日志输出,可以知道此时是在哪个数据源进行操作。如:Actual SQL: slave2
props:
sql-show: true
rules:
readwrite-splitting:
data-sources:
myds:
# 负载均衡算法名称 自定义
load-balancer-name: alg_round
props:
# 读数据源名称,多个从数据源用逗号分隔
read-data-source-names: slave1,slave2
# 写数据源名称
write-data-source-name: master
# 读写分离类型,如: Static,Dynamic
type: Static
load-balancers:
alg_random:
type: RANDOM
alg_round:
type: ROUND_ROBIN
alg_weight:
props:
slave1: 1
slave2: 2
type: WEIGHT
4、创建实体类
@TableName("t_user")
@Data
public class User {
@TableId(type = IdType.AUTO)
private Long id;
private String uname;
}
5、创建Mapper
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
6、测试
6.1:读写分离测试
@Autowired
private UserMapper userMapper;
/**
* 不添加@Transactional:insert对主库操作,select对从库操作
*/
@Test
public void insertTest() {
User user = new User();
user.setUname("lkx1");
user.setCreateTime(new Date());
userMapper.insert(user);
List<User> users = userMapper.selectList(null);
System.out.println(users);
}
效果:
Actual SQL: master ::: INSERT 可以看出insert语句实在master这个配置的数据源执行的
Actual SQL: slave1 ::: SELECT 可以看出查询实在slave1从库其中一个执行的
6.2:事务测试
/**
* 添加@Transactional:则insert和select均对主库操作
*/
@Test
@Transactional
public void insertOfTransactionalTest() {
User user = new User();
user.setUname("lkx_transactional");
user.setCreateTime(new Date());
userMapper.insert(user);
List<User> users = userMapper.selectList(null);
System.out.println(users);
}
效果:
可以看出insert与select都是在master数据源库进行处理的,然后因为添加了事务,所以在测试环境就会数据回滚
6.3:负载均衡读测试
/**
* 读数据测试
*/
@Test
public void testSelectAll(){
List<User> users1 = userMapper.selectList(null);
List<User> users2 = userMapper.selectList(null);//执行第二次测试负载均衡
}
效果:
可以看出两个从库每个执行一边select语句,我这使用的是轮询的算法。这里可以修改规则,有轮询、随机、权重三个规则。可以修改对应想要的查询算法
四、ShardingSphere-JDBC垂直分片
准备:使用docker创建两个容器
-
服务器:容器名
server-user
,端口3301
-
服务器:容器名
server-order
,端口3302
4.1、创建server-user容器
第一步:创建容器
docker run -d \
-p 3301:3306 \
-v /usr/local/docker/server/user/conf:/etc/mysql/conf.d \
-v /usr/local/docker/server/user/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-user \
mysql:8.0.29
第二步:登录MySQL服务器
#进入容器:
docker exec -it server-user env LANG=C.UTF-8 /bin/bash
#进入容器内的mysql命令行
mysql -uroot -p
#修改默认密码插件
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
第三步:创建数据库
CREATE DATABASE db_user;
USE db_user;
CREATE TABLE t_user (
id BIGINT AUTO_INCREMENT,
uname VARCHAR(30),
PRIMARY KEY (id)
);
4.2、创建server-order容器
第一步:创建容器
docker run -d \
-p 3302:3306 \
-v /usr/local/docker/server/order/conf:/etc/mysql/conf.d \
-v /usr/local/docker/server/order/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-order \
mysql:8.0.29
第二步:登录MySQL服务器
#进入容器:
docker exec -it server-order env LANG=C.UTF-8 /bin/bash
#进入容器内的mysql命令行
mysql -uroot -p
#修改默认密码插件
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
第三步:创建数据库
CREATE DATABASE db_user;
USE db_user;
CREATE TABLE t_user (
id BIGINT AUTO_INCREMENT,
uname VARCHAR(30),
PRIMARY KEY (id)
);
4.3、创建SpringBoot项目实现
4.3.1、引入maven
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
4.3.2、配置配置文件
server:
port: 8887
spring:
# 应用名称
application:
name: ShardingSphere-JDBC-Vertical-branch-library
# 开发环境设置
profiles:
active: dev
shardingsphere:
datasource:
# 配置真实数据源
names: server-user,server-order
server-user:
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://47.97.68.78:3301/db_user?characterEncoding=utf-8
password: 123456
type: com.zaxxer.hikari.HikariDataSource
username: root
server-order:
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://47.97.68.78:3302/db_order?characterEncoding=utf-8
password: 123456
type: com.zaxxer.hikari.HikariDataSource
username: root
# 内存模式
mode:
type: Memory
# 打印SQl 在控制台查看日志输出,可以知道此时是在哪个数据源进行操作。如:Actual SQL: slave2
props:
sql-show: true
rules:
sharding:
tables:
t_user:
# actual-data-nodes: server-user.t_user_${0..1}
actual-data-nodes: server-user.t_user
t_order:
actual-data-nodes: server-order.t_order
4.3.3、创建实体与Mapper文件
@TableName("t_order")
@Data
public class Order {
@TableId(type = IdType.AUTO)
private Long id;
private String orderNo;
private Long userId;
private BigDecimal amount;
}
@Mapper
public interface OrderMapper extends BaseMapper<Order> {
}
4.3.4、测试
【1】测试插入
@Autowired
private OrderMapper orderMapper;
@Autowired
private UserMapper userMapper;
@Test
void testInsertUserAndOrder(){
User user = new User();
user.setUname("lkx777");
user.setCreateTime(new Date());
userMapper.insert(user);
Order order = new Order();
order.setUserId(user.getId());
order.setOrderNo("O123457");
order.setAmount(new BigDecimal("100"));
orderMapper.insert(order);
}
效果:
由此可见,插入的时候是插入到不同的库中。
【2】测试查询
/**
* 垂直分片:查询数据测试
*/
@Test
public void testSelectFromOrderAndUser(){
User user = userMapper.selectById(1L);
Order order = orderMapper.selectById(1L);
}
效果:
五、ShardingSphere-JDBC水平分片(*重点*)
准备:使用docker创建两个容器
5.1、创建server-order0容器
第一步:创建容器
-
服务器:容器名
server-order0
,端口3310
-
服务器:容器名
server-order1
,端口3311
docker run -d \
-p 3310:3306 \
-v /usr/local/docker/server/order0/conf:/etc/mysql/conf.d \
-v /usr/local/docker/server/order0/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-order0 \
mysql:8.0.29
第二步:登录MySQL服务器
#进入容器:
docker exec -it server-order0 env LANG=C.UTF-8 /bin/bash
#进入容器内的mysql命令行
mysql -uroot -p
#修改默认密码插件
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
第三步:创建数据库
注意:
水平分片的id需要在业务层实现,不能依赖数据库的主键自增(否则不同库会出现相同的主键)
CREATE DATABASE db_order;
USE db_order;
CREATE TABLE t_order0 (
id BIGINT,
order_no VARCHAR(30),
user_id BIGINT,
amount DECIMAL(10,2),
PRIMARY KEY(id)
);
CREATE TABLE t_order1 (
id BIGINT,
order_no VARCHAR(30),
user_id BIGINT,
amount DECIMAL(10,2),
PRIMARY KEY(id)
);
5.2、创建server-order1容器
第一步:创建容器
docker run -d \
-p 3311:3306 \
-v /usr/local/docker/server/order1/conf:/etc/mysql/conf.d \
-v /usr/local/docker/server/order1/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
--name server-order1 \
mysql:8.0.29
第二步:登录MySQL服务器
#进入容器:
docker exec -it server-order1 env LANG=C.UTF-8 /bin/bash
#进入容器内的mysql命令行
mysql -uroot -p
#修改默认密码插件
ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
第三步:创建数据库
注意:
水平分片的id需要在业务层实现,不能依赖数据库的主键自增(否则不同库会出现相同的主键)
CREATE DATABASE db_order;
USE db_order;
CREATE TABLE t_order0 (
id BIGINT,
order_no VARCHAR(30),
user_id BIGINT,
amount DECIMAL(10,2),
PRIMARY KEY(id)
);
CREATE TABLE t_order1 (
id BIGINT,
order_no VARCHAR(30),
user_id BIGINT,
amount DECIMAL(10,2),
PRIMARY KEY(id)
);
5.3、创建SpringBoot项目实现
5.3.1、引入maven
<dependencies>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.20</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.1.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</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>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
5.3.2、配置配置文件
总配置文件:
server:
port: 8886
spring:
# 应用名称
application:
name: demoShardingSphere-JDBC-horizontal-fragmentation
# 开发环境设置
profiles:
active: dev
shardingsphere:
datasource:
# 配置真实数据源
names: server-user,server-order0,server-order1
server-user:
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://47.97.68.78:3301/db_user?characterEncoding=utf-8
password: 123456
type: com.zaxxer.hikari.HikariDataSource
username: root
server-order0:
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://47.97.68.78:3310/db_order?characterEncoding=utf-8
password: 123456
type: com.zaxxer.hikari.HikariDataSource
username: root
server-order1:
driver-class-name: com.mysql.jdbc.Driver
jdbc-url: jdbc:mysql://47.97.68.78:3311/db_order?characterEncoding=utf-8
password: 123456
type: com.zaxxer.hikari.HikariDataSource
username: root
# 内存模式
mode:
type: Memory
# 打印SQl 在控制台查看日志输出,可以知道此时是在哪个数据源进行操作。如:Actual SQL: slave2
props:
sql-show: true
# spring.shardingsphere.rules.sharding.tables.<table-name>.actual-data-nodes=值
# 值由数据源名 + 表名组成,以小数点分隔。多个表以逗号分隔,支持 inline 表达式。 server-order$->{0..1}.t_order$->{0..1}
# <table-name>:逻辑表名
rules:
sharding:
tables:
t_user:
# actual-data-nodes: server-user.t_user_${0..1}
actual-data-nodes: server-user.t_user
t_order:
# actual-data-nodes: server-order0.t_order0,server-order0.t_order1,server-order1.t_order0,server-order1.t_order1
actual-data-nodes: server-order$->{[0,1]}.t_order$->{[0,1]}
# actual-data-nodes: server-order$->{[0,1]}.t_order0
# ---------------分库策略
database-strategy:
standard:
# 分片列名称
sharding-column: user_id
# 分片算法名称
sharding-algorithm-name: alg_inline_userid
# ---------------分表策略
table-strategy:
standard:
# 分片列名称
sharding-column: order_no
# 分片算法名称
sharding-algorithm-name: alg_hash_mod
#------------------------分布式序列策略配置
key-generate-strategy:
# 分布式序列列名称
column: id
# 分布式序列算法名称
key-generator-name: alg_snowflake
t_order_item:
actual-data-nodes: server-order$->{[0,1]}.t_order_item$->{[0,1]}
# ---------------分库策略
database-strategy:
standard:
# 分片列名称
sharding-column: user_id
# 分片算法名称
sharding-algorithm-name: alg_inline_userid
# ---------------分表策略
table-strategy:
standard:
# 分片列名称
sharding-column: order_no
# 分片算法名称
sharding-algorithm-name: alg_hash_mod
#------------------------分布式序列策略配置
key-generate-strategy:
# 分布式序列列名称
column: id
# 分布式序列算法名称
key-generator-name: alg_snowflake
t_dict:
actual-data-nodes: server-user.t_dict,server-order$->{[0,1]}.t_dict
sharding-algorithms:
# 行表达式分片算法 alg_inline_userid 是取的对应的算法名称,这里可自定义
alg_inline_userid:
# 分片算法类型
type: INLINE
# 分片算法属性配置
props:
algorithm-expression: server-order$->{user_id % 2}
# 取模分片算法 alg_mod 是取的对应的算法名称,这里可自定义
alg_mod:
# 分片算法类型
type: MOD
# 分片算法属性配置
props:
sharding-count: 2
alg_hash_mod:
type: HASH_MOD
props:
sharding-count: 2
# 分布式序列算法配置
key-generators:
alg_snowflake:
# 分布式序列算法类型
type: SNOWFLAKE
# 绑定表规则列表
#使用绑定表进行多表关联查询时,必须使用分片键(user_id,order_no)进行关联,否则会出现笛卡尔积关联或跨库关联,从而影响查询效率。
binding-tables[0]: t_order,t_order_item
# 广播表
broadcast-tables[0]: t_dict
分库配置:
spring:
shardingsphere:
rules:
sharding:
#------------------------分片算法配置
sharding-algorithms:
alg_inline_userid:
# 分片算法属性配置
props:
algorithm-expression: server-order$->{user_id % 2}
# 分片算法类型
type: INLINE
alg_mod:
# 分片算法属性配置
props:
sharding-count: 2
# 分片算法类型
type: MOD
tables:
t_order:
#------------------------分库策略
database-strategy:
standard:
# 分片算法名称
sharding-algorithm-name: alg_inline_userid
# 分片列名称
sharding-column: user_id
分表配置:
spring:
shardingsphere:
rules:
sharding:
#------------------------分片算法配置
# 哈希取模分片算法
sharding-algorithms:
alg_hash_mod:
# 分片算法属性配置
props:
sharding-count: 2
# 分片算法类型
type: HASH_MOD
tables:
t_order:
#------------------------分库策略
table-strategy:
standard:
# 分片算法名称
sharding-algorithm-name: alg_hash_mod
# 分片列名称
sharding-column: order_no
5.3.3、测试
【1】插入测试
/**
* 水平分片:分表插入数据测试
*/
@Test
public void testInsertOrderTableStrategy(){
for (long i = 100; i < 104; i++) {
Order order = new Order();
order.setOrderNo("O" + i);
order.setUserId(1L);
order.setAmount(new BigDecimal(100));
orderMapper.insert(order);
}
for (long i = 105; i < 109; i++) {
Order order = new Order();
order.setOrderNo("O" + i);
order.setUserId(2L);
order.setAmount(new BigDecimal(100));
orderMapper.insert(order);
}
}
效果:
【2】查询测试
/**
* 水平分片:查询所有记录
* 查询了两个数据源,每个数据源中使用UNION ALL连接两个表
*/
@Test
public void testShardingSelectAll(){
List<Order> orders = orderMapper.selectList(null);
orders.forEach(System.out::println);
}
/**
* 水平分片:根据user_id查询记录
* 查询了一个数据源,每个数据源中使用UNION ALL连接两个表
*/
@Test
public void testShardingSelectByUserId(){
QueryWrapper<Order> orderQueryWrapper = new QueryWrapper<>();
orderQueryWrapper.eq("user_id", 1L);
List<Order> orders = orderMapper.selectList(orderQueryWrapper);
orders.forEach(System.out::println);
}
效果:有一些我的老数据可忽略结果,直接看sql
5.4、多表关联
5.4.1、创建关联表
在server-order0、server-order1
服务器中分别创建两张订单详情表t_order_item0、t_order_item1
我们希望同一个用户的订单表和订单详情表中的数据都在同一个数据源中,避免跨库关联
,因此这两张表我们使用相同的分片策略。
那么在t_order_item
中我们也需要创建order_no
和user_id
这两个分片键
CREATE TABLE t_order_item0(
id BIGINT,
order_no VARCHAR(30),
user_id BIGINT,
price DECIMAL(10,2),
`count` INT,
PRIMARY KEY(id)
);CREATE TABLE t_order_item1(
id BIGINT,
order_no VARCHAR(30),
user_id BIGINT,
price DECIMAL(10,2),
`count` INT,
PRIMARY KEY(id)
);
5.4.2、创建实体类与Mapper
@TableName("t_order_item")
@Data
public class OrderItem {
//当配置了shardingsphere-jdbc的分布式序列时,自动使用shardingsphere-jdbc的分布式序列
@TableId(type = IdType.AUTO)
private Long id;
private String orderNo;
private Long userId;
private BigDecimal price;
private Integer count;
}
@Mapper
public interface OrderItemMapper extends BaseMapper<OrderItem> {
}
5.4.3、关联表相关配置
spring:
shardingsphere:
rules:
sharding:
tables:
t_order_item:
#------------------------标准分片表配置(数据节点配置)
actual-data-nodes: server-order$->{0..1}.t_order_item$->{0..1}
#------------------------分库策略
database-strategy:
standard:
# 分片算法名称
sharding-algorithm-name: alg_mod
# 分片列名称
sharding-column: user_id
#------------------------分布式序列策略配置
key-generate-strategy:
# 分布式序列列名称
column: id
# 分布式序列算法名称
key-generator-name: alg_snowflake
#------------------------分表策略
table-strategy:
standard:
# 分片算法名称
sharding-algorithm-name: alg_hash_mod
# 分片列名称
sharding-column: order_no
5.4.4、测试
【1】插入测试
/**
* 测试关联表插入
*/
@Test
public void testInsertOrderAndOrderItem(){
for (long i = 1; i < 3; i++) {
Order order = new Order();
String orderNo = "O" + i;
order.setOrderNo(orderNo);
order.setUserId(1L);
orderMapper.insert(order);
for (long j = 1; j < 3; j++) {
OrderItem orderItem = new OrderItem();
orderItem.setOrderNo(orderNo);
orderItem.setUserId(1L);
orderItem.setPrice(new BigDecimal(10));
orderItem.setCount(2);
orderItemMapper.insert(orderItem);
}
}
for (long i = 5; i < 7; i++) {
Order order = new Order();
String orderNo = "O" + i;
order.setOrderNo(orderNo);
order.setUserId(2L);
orderMapper.insert(order);
for (long j = 1; j < 3; j++) {
OrderItem orderItem = new OrderItem();
orderItem.setOrderNo(orderNo);
orderItem.setUserId(2L);
orderItem.setPrice(new BigDecimal(1));
orderItem.setCount(3);
orderItemMapper.insert(orderItem);
}
}
}
【2】查询测试
@Data
public class OrderVo {
private String orderNo;
private BigDecimal amount;
}
/**
* 测试关联表查询
*/
@Test
public void testGetOrderAmount(){
List<OrderVo> orderAmountList = orderMapper.getOrderAmount();
orderAmountList.forEach(System.out::println);
}
<select id="getOrderAmount" resultType="com.lkx.horizontalfragmentation.entity.OrderVo">
SELECT o.order_no, SUM(i.price * i.count) AS amount
FROM t_order o JOIN t_order_item i ON o.order_no = i.order_no
GROUP BY o.order_no
</select>
5.5、配置绑定表
配置:
spring:
shardingsphere:
rules:
sharding:
binding-tables[0]: t_order,t_order_item
配置完绑定表后再次进行关联查询的测试:
-
如果不配置绑定表:测试的结果为8个SQL。多表关联查询会出现笛卡尔积关联。
-
如果配置绑定表:测试的结果为4个SQL。 多表关联查询不会出现笛卡尔积关联,关联查询效率将大大提升。
绑定表:
指分片规则一致的一组分片表。 使用绑定表进行多表关联查询时,必须使用分片键进行关联,否则会出现笛卡尔积关联或跨库关联,从而影响查询效率。
六、ShardingSphere-Proxy