从0开始学习shardingjdbc之分库分表
最近笔者在找java后台工作,经常在面试的过程中被问到分库分表的相关知识,因此自己本地跑了一个项目,完成了分库分表。 源代码地址:github.com/faxuexiaoxi… 该项目包含了其他的内容,例如只使用mybatis druid实现的增删改查的standardDemo。本项目的内容是simpleDemo
分库分表项目简单介绍:通过springboot + shardingjdbc+mybatis plus+ druid完成查询,插入数据能分别路由到指定的表中。如下图所示
下面简单介绍下用到的框架以及版本
| 框架 | 版本 |
|---|---|
| springboot | 3.2.2 |
| mybatis | 3.0.3 |
| mybatis plus | 3.5.5 |
| druid | 1.2.20 |
| shardingjdbc | 5.2.1 |
一般来说,mybatis和mybatis plus只要兼容springboot 3.0版本即可 sharding jdbc由于版本更新比较快,建议用稳定版本。
- 下面是maven的包的引入,可以一键复制。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- Mybatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<!-- Mybatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-spring-boot3-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Druid 数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.20</version>
</dependency>
<!-- shardingsphere-->
<dependency>
<groupId>org.apache.shardingsphere</groupId>
<artifactId>shardingsphere-jdbc-core-spring-boot-starter</artifactId>
<version>5.2.1</version>
</dependency>
<!-- shardingsphere和2.2不兼容,需要使用1.33-->
<dependency>
<groupId>org.yaml</groupId>
<artifactId>snakeyaml</artifactId>
<version>1.33</version>
</dependency>
2. mysql数据库的搭建,建表语句如下。建表语句init.sql在resource目录下
DROP TABLE IF EXISTS `user_0`;
CREATE TABLE if not exists `user_0`
(
`userId` BIGINT NOT NULL AUTO_INCREMENT,
`userName` VARCHAR(45) NOT NULL,
`userPassword` VARCHAR(45) NOT NULL,
`userPassword_` VARCHAR(45) NOT NULL,
PRIMARY KEY (`userId`)
);
DROP TABLE IF EXISTS `user_1`;
CREATE TABLE if not exists `user_1`
(
`userId` BIGINT NOT NULL AUTO_INCREMENT,
`userName` VARCHAR(45) NOT NULL,
`userPassword` VARCHAR(45) NOT NULL,
`userPassword_` VARCHAR(45) NOT NULL,
PRIMARY KEY (`userId`)
)
- 主要注意的地方如下:由于使用分库分表的shardingjdbc需要自己配置分布式主键,所以一般来说分布式主键的生成规则都是雪花算法,生成的数字都是需要8字节存储,因此需要BIGINT。由于是demo,userId的自增属性没有实际意义。
- 上述建表语句没有指定数据库,可能需要玩家自行创建数据库。例如:create database m0;use m0;
- 字段含义如下:userId表示玩家的id,username玩家名称,userpassword玩家密码,userpassword_表示加密后的密码,shardingjdbc加密功能会在下一篇文章中讲到,本文章只设计分库分表。
数据库格式如下所示即创建成功。需要在sharding和sharding_tmp中分别创建user_0,user_1
- 下面是编写项目代码,首先是实体类 User。
- 主键的字段必须为LONG,不能long。否则sharding-jdbc不能生成主键
- 主键字段不能有TableId注解,TableId注解会导致使用mybatis配置的主键生成策略(如果使用mybatis的主键生成策略也能满足需求的话,用@TableId似乎也无妨,如果要映射字段可以使用@TableField来指定)
- 主键字段尽量全部小写,不要有特殊符号例如下划线。否则在sharding jdbc配置分表策略中,会提示找不到字段。例如:主键如果是UserId,报错提示 找不到数据库的userid字段....
package org.faxuexiaoxin.simpledemo.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("user")
public class User {
/**
* 玩家id
*/
// @TableId(value = "userId")
@TableField("userId")
private Long userid;
/**
* 玩家名称
*/
@TableField("userName")
private String userName;
/**
* 玩家密码
*/
@TableField("userPassword")
private String userPassword;
/**
* 玩家加密后的密码
*/
@TableField("userPassword_")
private String userPassword_;
public Long getUserid() {
return userid;
}
public void setUserid(Long userid) {
this.userid = userid;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserPassword() {
return userPassword;
}
public void setUserPassword(String userPassword) {
this.userPassword = userPassword;
}
public String getUserPassword_() {
return userPassword_;
}
public void setUserPassword_(String userPassword_) {
this.userPassword_ = userPassword_;
}
}
下面是mapper,使用mybatis plus提供的BaseMapper即可
package org.faxuexiaoxin.simpledemo.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.faxuexiaoxin.simpledemo.entity.User;
public interface UserMapper extends BaseMapper<User> {
}
最后是入口类,扫码mapper即可
package org.faxuexiaoxin.simpledemo;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan(basePackages = "org.faxuexiaoxin.simpledemo.mapper")
public class SimpleDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SimpleDemoApplication.class, args);
}
}
编写测试用例 UserMapperTest,批量插入10个数据:
package org.faxuexiaoxin.simpledemo.mapper;
import org.faxuexiaoxin.simpledemo.entity.User;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void testInsert() {
User user = new User();
for (int i = 0; i < 10; i++) {
// user.setUserid(i);
user.setUserName("法学小心鑫");
user.setUserPassword("test");
user.setUserPassword_("fsdfsdfs");
userMapper.insert(user);
}
}
}
4. 下面修改application.properties.
如果只使用durid mybatis mybatis plus。那么需要按照springboot data中的配置url,username,password,datasource即可。 但是目前使用了shardingjdbc需要配置的属性会复杂很多。需要考虑下面问题。
1. 分库分到sharding 和 sharding_tmp两个数据库中,因此需要填写两个库的连接信息。笔者为了简单,两个库用的同一个数据库下的不同schema。
2. 分库是怎么分的,通过哪个字段来分。用户的主键id %2分别映射到sharding 和 sharding_tmp中
3. 分表是怎么分的,每个数据库中有user_0和user_1
4. 分表是怎么分的,通过哪个字段来分。用户的主键id %2分别映射到user_0和user_1中。
5. 主键的id是怎么生成的。雪花算法
spring.application.name=simpleDemo
#打印sql
spring.shardingsphere.props.sql-show=true
#配置sharding的数据源
spring.shardingsphere.datasource.names=m0,m1
spring.shardingsphere.datasource.m0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m0.dirver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m0.url=jdbc:mysql://localhost:3306/sharding
spring.shardingsphere.datasource.m0.username=root
spring.shardingsphere.datasource.m0.password=root
spring.shardingsphere.datasource.m1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.m1.dirver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.m1.url=jdbc:mysql://localhost:3306/sharding_tmp
spring.shardingsphere.datasource.m1.username=root
spring.shardingsphere.datasource.m1.password=root
#配置sharding需要的表
spring.shardingsphere.rules.sharding.tables.user.actual-data-nodes=m$->{0..1}.user_$->{0..1}
#分库策略
spring.shardingsphere.rules.sharding.tables.user.database-strategy.standard.sharding-column=userid
spring.shardingsphere.rules.sharding.tables.user.database-strategy.standard.sharding-algorithm-name= user_alg
spring.shardingsphere.rules.sharding.sharding-algorithms.user_alg.type= MOD
spring.shardingsphere.rules.sharding.sharding-algorithms.user_alg.props.sharding-count=2
#分表策略
spring.shardingsphere.rules.sharding.tables.user.table-strategy.standard.sharding-column=userid
spring.shardingsphere.rules.sharding.tables.user.table-strategy.standard.sharding-algorithm-name=userId-inline
spring.shardingsphere.rules.sharding.sharding-algorithms.userId-inline.type= INLINE
spring.shardingsphere.rules.sharding.sharding-algorithms.userId-inline.props.algorithm-expression=user_$->{userid%2}
#配置分布式id的算法
spring.shardingsphere.rules.sharding.tables.user.key-generate-strategy.column=userid
spring.shardingsphere.rules.sharding.tables.user.key-generate-strategy.key-generator-name=alg_snow
spring.shardingsphere.rules.sharding.key-generators.alg_snow.type=SNOWFLAKE
spring.shardingsphere.rules.sharding.key-generators.alg_snow.props.worker-id=1
5.本地运行测试
标红的地方表示执行的sql,数据生成到了数据库中,下面去数据库中查看下sharding的user_0
sharing_tmp的user_1
6.答疑:
- 为什么sharding的user_1和sharding_tmp的user_0,没有数据。因为分库是按照userId %2来分的,所以分到sharding的数据尾号都是偶数,分到sharding_tmp的都是奇数。同理分表也是按照这个方案来分的,导致每个库的表的数据有倾斜。后续可以修改算法来映射,这只是一个简单的例子。
- application.properties的属性配置的算法有哪些,需要填入哪些属性。下一节我们开始回答
- 代码中application.properties配置了sharding-algorithms,示例如下:那么总共有哪些呢?
spring.shardingsphere.rules.sharding.tables.user.database-strategy.standard.sharding-column=userid
spring.shardingsphere.rules.sharding.tables.user.database-strategy.standard.sharding-algorithm-name= user_alg
spring.shardingsphere.rules.sharding.sharding-algorithms.user_alg.type= HASH_MOD
spring.shardingsphere.rules.sharding.sharding-algorithms.user_alg.props.sharding-count=2
我去官方网站找了下文档,idea中可以搜索 org.apache.shardingsphere.sharding.spi.ShardingAlgorithm:共有下列算法
org.apache.shardingsphere.sharding.algorithm.sharding.inline.InlineShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.mod.ModShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.mod.HashModShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.range.VolumeBasedRangeShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.range.BoundaryBasedRangeShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.datetime.AutoIntervalShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.datetime.IntervalShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.classbased.ClassBasedShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.complex.ComplexInlineShardingAlgorithm
org.apache.shardingsphere.sharding.algorithm.sharding.hint.HintInlineShardingAlgorithm
其中
具体的用法官方网站没有举例~(ps:真的很不负责)