二 基本案例入门
- order.sql
DROP TABLE IF EXISTS t_order_1;
CREATE TABLE t_order_1(
order_id varchar(255) NOT NULL COMMENT '订单编号' ,
order_time DATETIME NOT NULL COMMENT '订单时间' ,
order_money DECIMAL(24,6) NOT NULL COMMENT '订单金额' ,
user_id INT NOT NULL COMMENT '订单下单人' ,
PRIMARY KEY (order_id)
) COMMENT = '订单表';
DROP TABLE IF EXISTS t_order_2;
CREATE TABLE t_order_2(
order_id INT NOT NULL COMMENT '订单编号' ,
order_time DATETIME NOT NULL COMMENT '订单时间' ,
order_money DECIMAL(24,6) NOT NULL COMMENT '订单金额' ,
user_id INT NOT NULL COMMENT '订单下单人' ,
PRIMARY KEY (order_id)
) COMMENT = '订单表';
- dict.sql
DROP TABLE IF EXISTS t_dict;
CREATE TABLE t_dict(
dict_id INT(11) NOT NULL AUTO_INCREMENT COMMENT '字典id' ,
dict_fid INT(11) COMMENT '所属字典;上级id;0:为字典类型,其余为该字典类型下的值' ,
dict_label VARCHAR(255) COMMENT '字典名称;字典名称' ,
dict_value VARCHAR(255) COMMENT '字典值' ,
dict_code VARCHAR(255) COMMENT '字典唯一编码' ,
create_time DATETIME COMMENT '创建时间' ,
remark VARCHAR(255) COMMENT '备注' ,
is_delete VARCHAR(1) NOT NULL DEFAULT 1 COMMENT '是否删除;0:已删除;1:未删除' ,
PRIMARY KEY (dict_id)
) COMMENT = '字典信息表';
2.1 基本结构搭建
- 依赖
<?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>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.shu</groupId>
<artifactId>ShardingSphereJdbcDemo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ShardingSphereJdbcDemo</name>
<description>ShardingSphereJdbcDemo</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- mybatis依赖 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.5</version>
</dependency>
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>4.0.0-RC1</version>
</dependency>
<!--swagger2依赖-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</project>
- 实体类
package com.shu.pojo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @description: 订单实体类
* @author: shu
* @createDate: 2022/8/4 15:13
* @version: 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Order {
/** 订单编号 */
@ApiModelProperty(name = "订单编号",notes = "")
private String orderId ;
/** 订单时间 */
@ApiModelProperty(name = "订单时间",notes = "")
@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")
private Date orderTime ;
/** 订单金额 */
@ApiModelProperty(name = "订单金额",notes = "")
private Double orderMoney ;
/** 订单下单人 */
@ApiModelProperty(name = "订单下单人",notes = "")
private Integer userId ;
}
package com.shu.pojo;
import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
/**
* @description: 字典表
* @author: shu
* @createDate: 2022/8/5 9:35
* @version: 1.0
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dict {
/** 字典id */
@ApiModelProperty(name = "字典id",notes = "")
private Integer dictId ;
/** 所属字典;上级id;0:为字典类型,其余为该字典类型下的值 */
@ApiModelProperty(name = "所属字典",notes = "上级id;0:为字典类型,其余为该字典类型下的值")
private Integer dictFid ;
/** 字典名称;字典名称 */
@ApiModelProperty(name = "字典名称",notes = "字典名称")
private String dictLabel ;
/** 字典值 */
@ApiModelProperty(name = "字典值",notes = "")
private String dictValue ;
/** 字典唯一编码 */
@ApiModelProperty(name = "字典唯一编码",notes = "")
private String dictCode ;
/** 创建时间 */
@ApiModelProperty(name = "创建时间",notes = "")
@JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")
private Date createTime ;
/** 备注 */
@ApiModelProperty(name = "备注",notes = "")
private String remark ;
/** 是否删除;0:已删除;1:未删除 */
@ApiModelProperty(name = "是否删除",notes = "0:已删除;1:未删除")
private String isDelete ;
}
- 其余的自行搭建
2.2 水平分表测试
- 首先我们需要在数据库中创建相同的两张表。
- 我们将要编写插入,与查询测试,因此我们需要编写sql
mapper
// 插入一条记录
@Insert("insert into t_order (order_time, order_money, user_id) values (#{orderTime}, #{orderMoney}, #{userId})")
int insert(Order order);
// 查询所有记录
@Select("select * from t_order")
List<Order> selectAll();
// 根据主键查询一条记录
@Select("select * from t_order where order_id = #{orderId}")
Order selectByPrimaryKey(String orderId);
service
package com.shu.service;
import com.shu.mapper.OrderTestMapper;
import com.shu.pojo.Order;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
/**
* @description:
* @author: shu
* @createDate: 2022/8/4 15:33
* @version: 1.0
*/
@Service
public class OrderService {
@Autowired()
OrderTestMapper orderTestMapper;
public int insert(Order order) {
return orderTestMapper.insert(order);
}
public List<Order> selectAll() {
return orderTestMapper.selectAll();
}
public Order selectByPrimaryKey(String orderId) {
return orderTestMapper.selectByPrimaryKey(orderId);
}
}
controller
package com.shu.controller;
import com.shu.pojo.Order;
import com.shu.service.OrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @description:
* @author: shu
* @createDate: 2022/8/4 15:34
* @version: 1.0
*/
@RestController
@Slf4j
public class OrderController {
@Autowired
OrderService orderService;
@PostMapping("/order")
public int insert(@RequestBody Order order) {
return orderService.insert(order);
}
@GetMapping("/order")
public List<Order> selectAll() {
return orderService.selectAll();
}
@GetMapping("/order/{orderId}")
public Order selectByPrimaryKey(@PathVariable("orderId") String orderId) {
return orderService.selectByPrimaryKey(orderId);
}
}
- 配置文件配置
在官网中,提供了中配置,我们可以根据自己的选择来决定选择那种配置。
- application.yaml配置
2.2.1 数据源配置
dataSources: # 数据源配置,可配置多个 <data-source-name>
<data-source-name>: # 数据源名称
dataSourceClassName: # 数据源完整类名
driverClassName: # 数据库驱动类名,以数据库连接池自身配置为准
jdbcUrl: # 数据库 URL 连接,以数据库连接池自身配置为准
username: # 数据库用户名,以数据库连接池自身配置为准
password: # 数据库密码,以数据库连接池自身配置为准
# ... 数据库连接池的其它属性
我们可以看到我们配置多个数据源,这里我们是水平分表
# 开启大驼峰命名法
mybatis:
configuration:
map-underscore-to-camel-case: true
# 分库分表配置
spring:
sharding-sphere:
datasource:
# 数据源名称
names: d1,m3
# 数据源d1配置
d1.type: com.alibaba.druid.pool.DruidDataSource
d1.url: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=utf8
d1.username: root
d1.password: 123456
d1.driverClassName: com.mysql.cj.jdbc.Driver
# 其他数据源配置
m3.type: com.alibaba.druid.pool.DruidDataSource
m3.url: jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=utf8
m3.username: root
m3.password: 123456
m3.driverClassName: com.mysql.cj.jdbc.Driver
2.2.2 数据分片配置
rules:
- !SHARDING
tables: # 数据分片规则配置
<logic-table-name> (+): # 逻辑表名称
actualDataNodes (?): # 由数据源名 + 表名组成(参考 Inline 语法规则)
databaseStrategy (?): # 分库策略,缺省表示使用默认分库策略,以下的分片策略只能选其一
standard: # 用于单分片键的标准分片场景
shardingColumn: # 分片列名称
shardingAlgorithmName: # 分片算法名称
complex: # 用于多分片键的复合分片场景
shardingColumns: # 分片列名称,多个列以逗号分隔
shardingAlgorithmName: # 分片算法名称
hint: # Hint 分片策略
shardingAlgorithmName: # 分片算法名称
none: # 不分片
tableStrategy: # 分表策略,同分库策略
keyGenerateStrategy: # 分布式序列策略
column: # 自增列名称,缺省表示不使用自增主键生成器
keyGeneratorName: # 分布式序列算法名称
autoTables: # 自动分片表规则配置
t_order_auto: # 逻辑表名称
actualDataSources (?): # 数据源名称
shardingStrategy: # 切分策略
standard: # 用于单分片键的标准分片场景
shardingColumn: # 分片列名称
shardingAlgorithmName: # 自动分片算法名称
bindingTables (+): # 绑定表规则列表
- <logic_table_name_1, logic_table_name_2, ...>
- <logic_table_name_1, logic_table_name_2, ...>
broadcastTables (+): # 广播表规则列表
- <table-name>
- <table-name>
defaultDatabaseStrategy: # 默认数据库分片策略
defaultTableStrategy: # 默认表分片策略
defaultKeyGenerateStrategy: # 默认的分布式序列策略
defaultShardingColumn: # 默认分片列名称
# 分片算法配置
shardingAlgorithms:
<sharding-algorithm-name> (+): # 分片算法名称
type: # 分片算法类型
props: # 分片算法属性配置
# ...
# 分布式序列算法配置
keyGenerators:
<key-generate-algorithm-name> (+): # 分布式序列算法名称
type: # 分布式序列算法类型
props: # 分布式序列算法属性配置
# ...
\
# 分片规则配置
sharding:
tables:
# 订单表
t_order.actual_data_nodes: d1.t_order_$->{1..2}
# 订单主键
t_order.key-generator.column: order_id
# 订单主键生成策略,主键生成策略为雪花算法
t_order.key-generator.type: Snowflake
# 定义分片键
t_order.table-strategy.inline.sharding-column: user_id
# 定义分片策略
t_order.table-strategy.inline.algorithm-expression: t_order_$->{user_id % 2 + 1}
- 完整配置
# 开启大驼峰命名法
mybatis:
configuration:
map-underscore-to-camel-case: true
# 分库分表配置
spring:
sharding-sphere:
datasource:
# 数据源名称
names: d1,m3
# 数据源d1配置
d1.type: com.alibaba.druid.pool.DruidDataSource
d1.url: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=utf8
d1.username: root
d1.password: 123456
d1.driverClassName: com.mysql.cj.jdbc.Driver
# 其他数据源配置
m3.type: com.alibaba.druid.pool.DruidDataSource
m3.url: jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=utf8
m3.username: root
m3.password: 123456
m3.driverClassName: com.mysql.cj.jdbc.Driver
# 分片规则配置
sharding:
tables:
# 订单表
t_order.actual_data_nodes: d1.t_order_$->{1..2}
# 订单主键
t_order.key-generator.column: order_id
# 订单主键生成策略,主键生成策略为雪花算法
t_order.key-generator.type: Snowflake
# 定义分片键
t_order.table-strategy.inline.sharding-column: user_id
# 定义分片策略
t_order.table-strategy.inline.algorithm-expression: t_order_$->{user_id % 2 + 1}
props:
sql:
show: true
logging:
level:
root: info
org.springframework.web: info
com.shu.mapper: debug
druid.sql: debug
- 插入测试
- 查询测试
2.2.3 结论
- 我们可以看到我们以userid作为分片键,当偶数插入第一张表,奇数插入第二张表
- 查询的时候,他会去执行两次查询,在合并结果,返回给我们
- 当然上面的主键生成策略,等等,可以自定义,后面在测试
2.3 水平分库测试
水平分库测试,首先准备两个数据库
- 整体框架一样,只是配置文件不一样
spring:
sharding-sphere:
datasource:
# 数据源名称
names: d1,d2,m3
# 数据源d1配置
d1.type: com.alibaba.druid.pool.DruidDataSource
d1.url: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=utf8
d1.username: root
d1.password: 123456
d1.driverClassName: com.mysql.cj.jdbc.Driver
# 数据源d2配置
d2.type: com.alibaba.druid.pool.DruidDataSource
d2.url: jdbc:mysql://localhost:3306/order_db_2?useUnicode=true&characterEncoding=utf8
d2.username: root
d2.password: 123456
d2.driverClassName: com.mysql.cj.jdbc.Driver
# 数据源d3配置
m3.type: com.alibaba.druid.pool.DruidDataSource
m3.url: jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=utf8
m3.username: root
m3.password: 123456
m3.driverClassName: com.mysql.cj.jdbc.Driver
sharding:
tables:
# 订单表
t_order.actual_data_nodes: d$->{1..2}.t_order_1
# 订单主键
t_order.key-generator.column: order_id
# 订单主键生成策略,主键生成策略为雪花算法
t_order.key-generator.type: Snowflake
# 定义分片键
t_order.database-strategy.inline.sharding-column: user_id
# 定义分片策略
t_order.database-strategy.inline.algorithm-expression: d$->{user_id % 2 + 1}
props:
sql:
show: true
测试
到这我们可以看到,ShardingSphere Jdbc帮我们解决了分库分表带来的一系列问题,当然这只是简单的使用,目的是对有个了解,详情请看后面笔记
2.4 数据源配置与分片配置
数据源配置
dataSources: # 数据源配置,可配置多个 <data-source-name>
<data-source-name>: # 数据源名称
dataSourceClassName: # 数据源完整类名
driverClassName: # 数据库驱动类名,以数据库连接池自身配置为准
jdbcUrl: # 数据库 URL 连接,以数据库连接池自身配置为准
username: # 数据库用户名,以数据库连接池自身配置为准
password: # 数据库密码,以数据库连接池自身配置为准
# ... 数据库连接池的其它属性
datasource:
# 数据源名称
names: d1,d2,m3
# 数据源d1配置
d1.type: com.alibaba.druid.pool.DruidDataSource
d1.url: jdbc:mysql://localhost:3306/order_db_1?useUnicode=true&characterEncoding=utf8
d1.username: root
d1.password: 123456
d1.driverClassName: com.mysql.cj.jdbc.Driver
# 数据源d2配置
d2.type: com.alibaba.druid.pool.DruidDataSource
d2.url: jdbc:mysql://localhost:3306/order_db_2?useUnicode=true&characterEncoding=utf8
d2.username: root
d2.password: 123456
d2.driverClassName: com.mysql.cj.jdbc.Driver
# 数据源d3配置
m3.type: com.alibaba.druid.pool.DruidDataSource
m3.url: jdbc:mysql://localhost:3306/auth?useUnicode=true&characterEncoding=utf8
m3.username: root
m3.password: 123456
m3.driverClassName: com.mysql.cj.jdbc.Driver
数据源配置比较简单
分片配置详细介绍
rules:
- !SHARDING
tables: # 数据分片规则配置
<logic-table-name> (+): # 逻辑表名称,比如你要进行分库分表的逻辑表名称,对应着你的数据库名称
actualDataNodes (?): # 由数据源名 + 表名组成(参考 Inline 语法规则)
# 分库策略,上面我们用的是 inline 内联分片策略配置
databaseStrategy (?): # 分库策略,缺省表示使用默认分库策略,以下的分片策略只能选其一
standard: # 用于单分片键的标准分片场景
shardingColumn: # 分片列名称
shardingAlgorithmName: # 分片算法名称
complex: # 用于多分片键的复合分片场景
shardingColumns: # 分片列名称,多个列以逗号分隔
shardingAlgorithmName: # 分片算法名称
hint: # Hint 分片策略
shardingAlgorithmName: # 分片算法名称
none: # 不分片
tableStrategy: # 分表策略,同分库策略
keyGenerateStrategy: # 分布式序列策略
column: # 自增列名称,缺省表示不使用自增主键生成器
keyGeneratorName: # 分布式序列算法名称
autoTables: # 自动分片表规则配置
t_order_auto: # 逻辑表名称
actualDataSources (?): # 数据源名称
shardingStrategy: # 切分策略
standard: # 用于单分片键的标准分片场景
shardingColumn: # 分片列名称
shardingAlgorithmName: # 自动分片算法名称
bindingTables (+): # 绑定表规则列表
- <logic_table_name_1, logic_table_name_2, ...>
- <logic_table_name_1, logic_table_name_2, ...>
broadcastTables (+): # 广播表规则列表
- <table-name>
- <table-name>
defaultDatabaseStrategy: # 默认数据库分片策略
defaultTableStrategy: # 默认表分片策略
defaultKeyGenerateStrategy: # 默认的分布式序列策略
defaultShardingColumn: # 默认分片列名称
# 分片算法配置
shardingAlgorithms:
<sharding-algorithm-name> (+): # 分片算法名称
type: # 分片算法类型
props: # 分片算法属性配置
# ...
# 分布式序列算法配置
keyGenerators:
<key-generate-algorithm-name> (+): # 分布式序列算法名称
type: # 分布式序列算法类型
props: # 分布式序列算法属性配置
# ...
- : 逻辑表名称,比如你要进行分库分表的逻辑表名称,对应着你的数据库名称,比如上面的t_order,在我们的数据库汇总t_order_1,或t_order_2,但是我们程序在sql语句中使操作的逻辑表
- actualDataNodes :真实的数据库源,由上面的数据源名称+表名称构成(参考参考 Inline 语法规则)
- databaseStrategy :分库策略,缺省表示使用默认分库策略,以下的分片策略只能选其一
当然不同的分库策略,配置参数不同。
- tableStrategy:分表策略:分表策略,同分库策略
- keyGenerateStrategy:主键生成策略
- broadcastTables:广播表规则列表,在分库分表中,有一些共用的表比如字典表
- binding-tables:绑定表规则列表
- default-database-strategy:默认分库策略
- default-table-strategy:默认分表策略
- default-key-generator:默认主键生成策略