接上一张分库分表分析。项目代码地址:https://gitee.com/wuhan1/sharding-jdbc.git
1.引入jar包
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
<relativePath />
</parent>
<!-- springboot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 添加 MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.2.0</version>
</dependency>
<!-- druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>
<!-- sharding-jdbc -->
<dependency>
<groupId>io.shardingsphere</groupId>
<artifactId>sharding-jdbc-spring-boot-starter</artifactId>
<version>3.1.0</version>
</dependency>
2.新建表t_user_1和t_user_2,表结构完全一样
3.新建工程sharding-jdbc,结构
mapper接口
public interface TUserMapper {
int insert(TUser user);
}
mapper接口对应的xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xqc.dao.TUserMapper" >
<insert id="insert" parameterType="TUser" >
INSERT INTO
t_user
(user_name,age,remark)
VALUES
(#{userName}, #{age}, #{remark})
</insert>
</mapper>
service接口
public interface TUserService {
boolean insert(TUser user);
}
service接口实现类
@Service
public class TUserServiceImpl implements TUserService{
@Autowired
private TUserMapper tUserMapper;
@Override
public boolean insert(TUser user) {
return this.tUserMapper.insert(user) == 1;
}
}
TUser实体类
public class TUser implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String userName;
private Integer age;
private String remark;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
}
Controller类
@RestController
public class TUserController {
@Autowired
private TUserService tUserService;
@GetMapping("/add")
public String add(){
TUser user = new TUser();
user.setUserName("张三");
user.setAge(20);
user.setRemark("...");
boolean result = this.tUserService.insert(user);
if(result){
return "success";
}
return "fail";
}
}
application.properties配置,本文采用的是sharding-jdbc-spring-boot-starter的3.1.0版本的,所以配置文件需以sharding.jdbc.开头
水平分表不分库配置
#spring.main.allow-bean-definition-overriding=true
#配置数据源的详细信息,多个逗号分开
sharding.jdbc.datasource.names=m1
# 数据库连接池
sharding.jdbc.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
#数据库驱动类名
sharding.jdbc.datasource.m1.driver-class-name=com.mysql.jdbc.Driver
# 数据库 URL 连接
sharding.jdbc.datasource.m1.url=jdbc:mysql://localhost:3306/sharding-jdbc?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
# 数据库用户名
sharding.jdbc.datasource.m1.username=root
# 数据库密码
sharding.jdbc.datasource.m1.password=123456
# 指定t_user表的分布情况,配置在哪个数据库,表名称是什么 m1.t_user_1 m1.t_user_2
sharding.jdbc.config.sharding.tables.t_user.actual-data-nodes=m1.t_user_$->{1..2}
#指定t_user表中主键的生成策略 SNOWFLAKE:雪花算法,随机生成主键Id
sharding.jdbc.config.sharding.tables.t_user.key-generator-column-name=id
sharding.jdbc.config.sharding.tables.t_user.key-generator-type=SNOWFLAKE
# 指定分片的策略 约定id值 : 偶数--》t_user_1 奇数-->t_user_2
sharding.jdbc.config.sharding.tables.t_user.table-strategy.inline.sharding-column=id
sharding.jdbc.config.sharding.tables.t_user.table-strategy.inline.algorithm-expression=t_user_$->{id%2 +1}
#打开sql的输出日志
sharding.jdbc.config.sharding.props.sql.show=true
#实体类扫描配置,这样在mapper的xml中可以直接使用实体类名,不用写包路径
mybatis.type-aliases-package=com.xqc.mode
#驼峰转换
mybatis.configuration.mapUnderscoreToCamelCase=true
#处理日期
spring.jackson.date-format=yyyy-MM-dd HH:mm
spring.jackson.time-zone=GMT+8
#xml文件扫描
mybatis.mapperLocations=classpath:mapper/*.xml
启动类配置
@SpringBootApplication
@MapperScan("com.xqc.dao")//mapper接口扫描
public class App {
public static void main( String[] args ){
SpringApplication.run(App.class, args);
}
}
4.浏览器多次访问http://localhost:8080/add查看数据库数据
主键ID尾数奇数的存在t_user_2表中
主键ID尾数偶数的存在t_user_1表中
上面数据存储采用的是自带的inline算法,我们也可以根据自己的需求,自定义分片算法
# 自定义分表算法
sharding.jdbc.config.sharding.tables.t_user.table-strategy.standard.sharding-column=id
sharding.jdbc.config.sharding.tables.t_user.table-strategy.standard.precise-algorithm-class-name=com.xqc.algorithm.MyPreciseShardingAlgorithm
实现接口PreciseShardingAlgorithm
/**
* @dateTime 创建时间:2020年8月19日 上午10:19:06
* @version V1.0.0
* 类说明 自定义分表算法,尾数0-5存表t_user_1,6-9存表t_user_2
*/
public class MyPreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long>{
private static final List<String> PARAM_LIST = Arrays.asList("0","1","2","3","4","5");
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
//availableTargetNames拆分出来的物理表【t_user_1,t_user_2】,shardingValue分片字段的值
for (String availableTargetName : availableTargetNames) {
String value = "2" ;
//尾数0-5存表t_user_1,6-9存表t_user_2
String colValue = shardingValue.getValue().toString();
String subLast = shardingValue.getValue().toString().substring(colValue.length()-1, colValue.length());
if(PARAM_LIST.contains(subLast)){
value = "1";
}
if(availableTargetName.endsWith(value)){
return availableTargetName;
}
}
throw new IllegalArgumentException();
}
}
再看数据库数据
分片算法介绍
通过分片算法将数据分片,支持通过=、BETWEEN和IN分片。分片算法需要应用方开发者自行实现,可实现的灵活度非常高。
精确分片算法
对应PreciseShardingAlgorithm,用于处理使用单一键作为分片键的=与IN进行分片的场景。需要配合StandardShardingStrategy使用。
范围分片算法
对应RangeShardingAlgorithm,用于处理使用单一键作为分片键的BETWEEN AND进行分片的场景。需要配合StandardShardingStrategy使用。
复合分片算法
对应ComplexKeysShardingAlgorithm,用于处理使用多键作为分片键进行分片的场景,包含多个分片键的逻辑较复杂,需要应用开发者自行处理其中的复杂度。需要配合ComplexShardingStrategy使用。
Hint分片算法
对应HintShardingAlgorithm,用于处理使用Hint行分片的场景。需要配合HintShardingStrategy使用。
水平分库分表配置:不同的数据库,相同的表结构,每个库都有多份
#分库不分表的情况
#spring.main.allow-bean-definition-overriding=true
#配置数据源的详细信息,多个逗号分开
sharding.jdbc.datasource.names=m1,m2
#数据库1
# 数据库连接池
sharding.jdbc.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
#数据库驱动类名
sharding.jdbc.datasource.m1.driver-class-name=com.mysql.jdbc.Driver
# 数据库 URL 连接
sharding.jdbc.datasource.m1.url=jdbc:mysql://localhost:3306/sharding-jdbc?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
# 数据库用户名
sharding.jdbc.datasource.m1.username=root
# 数据库密码
sharding.jdbc.datasource.m1.password=123456
#数据库2
# 数据库连接池
sharding.jdbc.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource
#数据库驱动类名
sharding.jdbc.datasource.m2.driver-class-name=com.mysql.jdbc.Driver
# 数据库 URL 连接
sharding.jdbc.datasource.m2.url=jdbc:mysql://localhost:3306/sharding-jdbc_02?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
# 数据库用户名
sharding.jdbc.datasource.m2.username=root
# 数据库密码
sharding.jdbc.datasource.m2.password=123456
# 指定分库的策略 约定id值 : 偶数--》m1 奇数-->m2
sharding.jdbc.config.sharding.tables.t_user.database-strategy.inline.sharding-column=id
sharding.jdbc.config.sharding.tables.t_user.database-strategy.inline.algorithm-expression=m$->{id%2 +1}
# 指定t_user表的分布情况,配置在哪个数据库,表名称是什么 m1.t_user_1 m1.t_user_2
sharding.jdbc.config.sharding.tables.t_user.actual-data-nodes=m$->{1..2}.t_user_$->{1..2}
#指定t_user表中主键的生成策略 SNOWFLAKE:雪花算法,随机生成主键Id
sharding.jdbc.config.sharding.tables.t_user.key-generator-column-name=id
sharding.jdbc.config.sharding.tables.t_user.key-generator-type=SNOWFLAKE
#指定表的分表算法
sharding.jdbc.config.sharding.tables.t_user.table-strategy.inline.sharding-column=pid
sharding.jdbc.config.sharding.tables.t_user.table-strategy.inline.algorithm-expression=t_user_$->{pid%2 +1}
#打开sql的输出日志
sharding.jdbc.config.sharding.props.sql.show=true
#实体类扫描配置,这样在mapper的xml中可以直接使用实体类名,不用写包路径
mybatis.type-aliases-package=com.xqc.mode
#驼峰转换
mybatis.configuration.mapUnderscoreToCamelCase=true
#处理日期
spring.jackson.date-format=yyyy-MM-dd HH:mm
spring.jackson.time-zone=GMT+8
#xml文件扫描
mybatis.mapperLocations=classpath:mapper/*.xml
水平分库不分表配置:相同的表结构,放在多个数据库中,根据分库算法存放数据,访问地址http://localhost:8080/role/add
#分库不分表的情况
#spring.main.allow-bean-definition-overriding=true
#配置数据源的详细信息,多个逗号分开
sharding.jdbc.datasource.names=m1,m2
#数据库1
# 数据库连接池
sharding.jdbc.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
#数据库驱动类名
sharding.jdbc.datasource.m1.driver-class-name=com.mysql.jdbc.Driver
# 数据库 URL 连接
sharding.jdbc.datasource.m1.url=jdbc:mysql://localhost:3306/sharding-jdbc?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
# 数据库用户名
sharding.jdbc.datasource.m1.username=root
# 数据库密码
sharding.jdbc.datasource.m1.password=123456
#数据库2
# 数据库连接池
sharding.jdbc.datasource.m2.type=com.alibaba.druid.pool.DruidDataSource
#数据库驱动类名
sharding.jdbc.datasource.m2.driver-class-name=com.mysql.jdbc.Driver
# 数据库 URL 连接
sharding.jdbc.datasource.m2.url=jdbc:mysql://localhost:3306/sharding-jdbc_02?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=UTC
# 数据库用户名
sharding.jdbc.datasource.m2.username=root
# 数据库密码
sharding.jdbc.datasource.m2.password=123456
# 指定t_user表的分布情况,配置在哪个数据库,表名称是什么 m1.t_user_1 m1.t_user_2
#sharding.jdbc.config.sharding.tables.t_role.actual-data-nodes=m1.t_user_$->{1..2}
#指定t_user表中主键的生成策略 SNOWFLAKE:雪花算法,随机生成主键Id
sharding.jdbc.config.sharding.tables.t_role.key-generator-column-name=id
sharding.jdbc.config.sharding.tables.t_role.key-generator-type=SNOWFLAKE
# 指定分片的策略 约定id值 : 偶数--》m1 奇数-->m2
# 自定义分库算法
sharding.jdbc.config.sharding.tables.t_role.database-strategy.inline.sharding-column=id
sharding.jdbc.config.sharding.tables.t_role.database-strategy.inline.algorithm-expression=m$->{id%2 +1}
#打开sql的输出日志
sharding.jdbc.config.sharding.props.sql.show=true
#实体类扫描配置,这样在mapper的xml中可以直接使用实体类名,不用写包路径
mybatis.type-aliases-package=com.xqc.mode
#驼峰转换
mybatis.configuration.mapUnderscoreToCamelCase=true
#处理日期
spring.jackson.date-format=yyyy-MM-dd HH:mm
spring.jackson.time-zone=GMT+8
#xml文件扫描
mybatis.mapperLocations=classpath:mapper/*.xml
sharding-jdbc执行流程
SQL路由:根据解析上下文匹配用户配置的分片策略,并生成最终的路由路径
SQL改写:将SQL改写为在真实数据库中可以正确执行的语句。