分库分表----sharding-jdbc和springboot按照时间分库分表(5年分一个库,库中一年分一个表)

昨天想写个道馆管理系统,会用到时间分库分表,想着先模拟一下,憋了好长时间终于憋出来了,记录一下。

1 技术:springboot,mybatis,sharding-jdbc,mysql

2 模拟:每五年为一个库,一个库中每一年为一张表用来记录招生人

1 数据库创建语句:

//后缀要为1,前面什么都无所谓,因为后期要用这个判断存入那哪数据库
CREATE DATABASE master04091

CREATE TABLE tab_user0(
id INT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
NAME VARCHAR(30) NOT NULL,
age INT(10) NOT NULL,
create_time DATETIME
)
CREATE TABLE tab_user1(
id INT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
NAME VARCHAR(30) NOT NULL,
age INT(10) NOT NULL,
create_time DATETIME
)

CREATE TABLE tab_user2(
id INT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
NAME VARCHAR(30) NOT NULL,
age INT(10) NOT NULL,
create_time DATETIME
)

CREATE TABLE tab_user3(
id INT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
NAME VARCHAR(30) NOT NULL,
age INT(10) NOT NULL,
create_time DATETIME
)

CREATE TABLE tab_user4(
id INT(20) NOT NULL AUTO_INCREMENT PRIMARY KEY,
NAME VARCHAR(30) NOT NULL,
age INT(10) NOT NULL,
create_time DATETIME
)
//之后再创建一个库名为 master04092,后缀要是2,前面什么都无所谓,因为后期要用这个判断存入那哪数据库,库中同样要有这5个表。

效果如下
分库分表----sharding-jdbc和springboot按照时间分库分表(5年分一个库,库中一年分一个表)_第1张图片
2 之后开始写代码
(1):pom.xml

    
        1.8
        1.8
        1.8
        2.1.3.RELEASE
        1.18.8
        4.12
    

    
        
            org.springframework.boot
            spring-boot-starter-web
            ${springframework.boot.version}
        

        
            org.springframework.boot
            spring-boot-starter-test
            ${springframework.boot.version}
            test
        
        
            org.springframework.boot
            spring-boot-test
            ${springframework.boot.version}
            test
        

        
            junit
            junit
            ${junit.version}
        

        
            org.mybatis.spring.boot
            mybatis-spring-boot-starter
            2.0.1
        

        
        
            org.apache.shardingsphere
            sharding-jdbc-spring-boot-starter
            4.0.0-RC1
        

        
            org.apache.commons
            commons-lang3
            3.4
        

        
            mysql
            mysql-connector-java
            5.1.38
        

        
            org.projectlombok
            lombok
            ${lombok.version}
        
    

(2):配置文件 application.peoperties

server.port=8082

#控制台打印sql
spring.shardingsphere.props.sql.show=true
spring.main.allow-bean-definition-overriding=true

#数据源 先创建2个数据库
spring.shardingsphere.datasource.names=master04091,master04092

# 配置master04091
spring.shardingsphere.datasource.master04091.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.master04091.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master04091.jdbc-url=jdbc:mysql://localhost:3306/master04091?characterEncoding=utf-8&&serverTimezone=GMT%2B8
spring.shardingsphere.datasource.master04091.username=root
spring.shardingsphere.datasource.master04091.password=1234

# 配置master04092
spring.shardingsphere.datasource.master04092.type=com.zaxxer.hikari.HikariDataSource
spring.shardingsphere.datasource.master04092.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.master04092.jdbc-url=jdbc:mysql://localhost:3306/master04092?characterEncoding=utf-8&&serverTimezone=GMT%2B8
spring.shardingsphere.datasource.master04092.username=root
spring.shardingsphere.datasource.master04092.password=1234

# 配置进行分库的字段
spring.shardingsphere.sharding.default-database-strategy.standard.sharding-column=create_time
# 具体的配置规则我们会在自定义类中指定,也就是这个PreciseModuloDatabaseShardingAlgorithm类,这写自定义类路径
spring.shardingsphere.sharding.default-database-strategy.standard.precise-algorithm-class-name=com.util.PreciseModuloDatabaseShardingAlgorithm

# 配置库与表结合,就是你有几个库,几个表,例如:我有master04091和mater04092这2个库,每个库下有五张表,tab_user0......tab_user4,并且指定了逻辑库为master0409,逻辑表为tab_user
spring.shardingsphere.sharding.tables.tab_user.actual-data-nodes=master0409$->{1..2}.tab_user$->{0..4}
# 配置进行分表的字段
spring.shardingsphere.sharding.default-table-strategy.standard.sharding-column=create_time
# 具体的配置规则我们会在自定义类中指定,也就是这个PreciseModuloTableShardingAlgorithm类,这写自定义类路径
spring.shardingsphere.sharding.default-table-strategy.standard.precise-algorithm-class-name=com.util.PreciseModuloTableShardingAlgorithm

(3)实体类:

import com.fasterxml.jackson.annotation.JsonFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.format.annotation.DateTimeFormat;

import java.util.Date;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
	private int id;
    private String name ;
	private int age;
	
	//保证给到数据库的时间是date类型,显示到前台的时间是字符串类型
	@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
	@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
	private String create_time;
}

(4)mapper和service层

//mapper
import com.bean.User;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.springframework.stereotype.Repository;

import java.util.Date;
import java.util.List;

@Repository
public interface UserMa {

	/**
	 * 添加,只需向逻辑表中添加即可,
	 */
	@Insert({
			" INSERT INTO tab_user (id,name,age,create_time) ",
			" VALUES ( " ,
			"#{id,jdbcType=INTEGER}, ",
			"#{name,jdbcType=VARCHAR}, ",
			"#{age,jdbcType=INTEGER}, ",
			"#{create_time,jdbcType=VARCHAR})"
	})
	int addUser(@Param("id")  Integer id,
				@Param("name") String name,
				@Param("age") Integer age,
				@Param("create_time") String create_time);
}

//service接口
import com.bean.User;
import com.util.ResponseUser;
public interface UserSer {
	ResponseUser addUser(User user);
}

//service实现
import com.bean.User;
import com.mapper.UserMa;
import com.service.imp.UserSer;
import com.util.ResponseUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;

@Service
public class UserSerDao implements UserSer {

	@Autowired
	private ResponseUser resUser;
	@Autowired
	private UserMa userMa;
	@Override
	public ResponseUser addUser(User user) {
		int count=userMa.addUser(
				user.getId(),user.getName(),user.getAge(),user.getCreate_time());
		if(count==0)
			resUser.setAll(1,"添加失败",null);
		else
			resUser.setAll(0,"添加成功",count);
		return resUser;
	}
}

(5)自定义规则类

//分库的自定义类
package com.util;
import org.apache.commons.lang3.StringUtils;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.Date;

//实现自定义接口
public class PreciseModuloDatabaseShardingAlgorithm
		implements PreciseShardingAlgorithm {
	/**
	 * @param collection 存放的是所有的库的列表,这里代表master04091,master04092
	 *                   master04091(2020~2024),master04092(2025~2029)
	 * @return 将数据写入的哪个库
	 */
	@Override
	public String doSharding(Collection collection,
							 PreciseShardingValue pre) {
		try {
			//配置的分库分片的sharding-column对应的值,也就是具体时间
			String str=pre.getValue());
			if (str.isEmpty()) {
				throw new UnsupportedOperationException("pre is null");
			}
			//each为每个库的名字
			for (String each:collection) {
				//得到具体年,截取字符串要头不要尾
				String value=StringUtils.substring(str,0,4);
				//以5年为一个库,例如:2020~2024
				int c=Integer.parseInt(value)-2020;
				//算差值,拿当前时间减去2020,用差除于5,会得到小数,用int强转,只要整数,这样为0的就放到1库,为1的就放到2库
				int database_hou_zhui=c/5;//0.2(2021年放到1库),1.2(2026年放到2库)
				if(each.endsWith(Integer.toString(database_hou_zhui+1))){
					//扔到后缀是database_hou_zhui+1的库,判断当前这个库是否符合我条件,
					// 而不是我去找符合我条件的库,因为没法找,好多库呢
					return each;
				}
			}
		}catch(ParseException e){
				e.printStackTrace();
		}
			return null;
	}
}

//分表的自定义规则类
package com.util;

import org.apache.commons.lang3.StringUtils;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;

import java.util.Collection;

/**
 * 分表策略,每5年放到一个库中,库中一年一个表
 */
public class PreciseModuloTableShardingAlgorithm
		implements PreciseShardingAlgorithm {
	@Override
	public String doSharding(Collection collection,
							 PreciseShardingValue prec) {
		//对于库的分片collection存放的是所有的库的列表,这里代表master04091,master04092
		//配置的分片的sharding-column对应的值
		String timeValue = prec.getValue();
		//判断timeValue是否为空
		if(StringUtils.isBlank(timeValue)){
			throw new UnsupportedOperationException("prec is null");
		}
		//按年路由,一个库中有5年的数据,在库中我们将每年分成一个表,
		// 对5取余,判断得到的所有表名后缀是否与取余一致,一致就返回
		for (String each:collection) {
			//得到具体年,截取字符串要头不要尾
			String value=StringUtils.substring(timeValue,0,4);
			int c=Integer.parseInt(value);
			//循环每个库,看哪个库与当前条件匹配
			if(each.endsWith(Integer.toString(c%5))){
				return each;
			}
		}
		return null;
	}
}

(6) 控制类

import com.bean.User;
import com.service.UserSerDao;
import com.util.ResponseUser;
import com.util.ToolUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;
import java.util.Date;


@RestController
public class UserCon {

	@Autowired
	private UserSerDao userSer;
	@Autowired
	private ResponseUser responseUser;

	/**
	 * 模拟插入数据
	 */
	User userList = new User();
	/**
	 * 初始化插入数据,@PostConstruct这个注解代表类实施事就执行这个方法,类似于构造函数(仅仅是我的理解)
	 */
	@PostConstruct
	private void getData() {
		SimpleDateFormat sfEnd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		userList=new User(3,"dadada", 298,"2025-05-15 14:39:18");//会发现被存入第二个库中tab_user0表中
		//userList=new User(3,"dadada", 298,sfEnd.format(new Date()));当前时间2020年,会发现被存入第一个库中tab_user0表中
		System.out.println("测试~"+userList.getCreate_time());
	}

	/**
	 * 添加数据,
	 */
	@GetMapping("/save-user")
	public Object saveUser() {
		return userSer.addUser(userList);
	}

3 完成
访问 http://localhost:8082/save-user ,即可添加数据。结果如下:
分库分表----sharding-jdbc和springboot按照时间分库分表(5年分一个库,库中一年分一个表)_第2张图片
总结,
1)如果是普通的按照id取模,年纪取模,直接可以在application文件中指定,例如:

#按照id对2取模 分库
spring.shardingsphere.sharding.default-database-strategy.inline.sharding-column=id
spring.shardingsphere.sharding.default-database-strategy.inline.algorithm-expression=master0409$->{id % 2}

#按照年龄对2取模 分表
spring.shardingsphere.sharding.tables.tab_user.actual-data-nodes=master0409$->{0..1}.tab_user$->{0..1}
spring.shardingsphere.sharding.tables.tab_user.table-strategy.inline.sharding-column=age
spring.shardingsphere.sharding.tables.tab_user.table-strategy.inline.algorithm-expression=tab_user$->{age % 2}

2)如果需要自定义规则控制分库分表,则需要在application文件中指定规则类的路径

# 定义分库字段
spring.shardingsphere.sharding.default-database-strategy.standard.sharding-column=create_time
# 指定分库规则的 类的 路径
spring.shardingsphere.sharding.default-database-strategy.standard.precise-algorithm-class-name=com.util.PreciseModuloDatabaseShardingAlgorithm


spring.shardingsphere.sharding.tables.tab_user.actual-data-nodes=master0409$->{1..2}.tab_user$->{0..4}
# 定义分表字段
spring.shardingsphere.sharding.default-table-strategy.standard.sharding-column=create_time
# 指定分表规则的 类的 路径
spring.shardingsphere.sharding.default-table-strategy.standard.precise-algorithm-class-name=com.util.PreciseModuloTableShardingAlgorithm


大神链接

https://blog.csdn.net/qq_34227896/article/details/102874568
https://blog.csdn.net/qq_34227896/article/details/102874568
https://blog.csdn.net/qq_34227896/article/details/102874568

你可能感兴趣的:(分库分表)