Sharding-JDBC 分库分表实战 springboot+sharding-jdbc+jpa+druid

  • 前言

系统采用mysql数据库,技术框架采用基于springboot的springcloud,持久层采用jpa,数据库连接池阿里druid

随着最近系统用户量增加到500万,等各系统用户过来后会超过2000万,这个时候随着用户表数据量的增加,

用户表查询效率 低下。这个时候未雨绸缪,开始准备分库分表的准备,采用分布式数据库中间件,选择了sharding spshare,

这篇文章主要是用到了sharding-jdbc进行分库分表,我们用户人数预计最高2000万,我们采用的是分库单表方案,即6个库,一个单表

  • 分库分表

1我选用的是基于springboot的配置规则,本文省略druid的配置

首先需要引入依赖


		UTF-8
		UTF-8
		1.8
		4.0.0-RC1
	

	
		
		
			org.apache.shardingsphere
			sharding-jdbc-spring-boot-starter
			${sharding-sphere.version}
		
		
			org.apache.shardingsphere
			sharding-jdbc-spring-namespace
			${sharding-sphere.version}
		

		
		
			org.springframework.boot
			spring-boot-starter-data-jpa
		
		
		
			mysql
			mysql-connector-java
			5.1.47
		
		
		
			com.alibaba
			druid
			1.1.16
		
		
			com.alibaba
			fastjson
			1.2.47
		
		
			org.projectlombok
			lombok
			true
		
		
			org.springframework.boot
			spring-boot-starter-web
		
		
			org.springframework.boot
			spring-boot-starter-test
			test
		
		
			commons-beanutils
			commons-beanutils
			1.9.4
		
	

 2 实体 user 和实体 address 表  user表主键sid也是用户的唯一id,最开始系统采用的sid用36位uuid去掉下划线后使用。现在先不评论uuid优缺点,先实现uuid作为分表字段。实现分库分表功能。

package com.bmw.cop.user.entity;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

@Entity
@Table(name ="sys_user")
@Data
@EntityListeners(AuditingEntityListener.class)
public class User implements Serializable {

    /**
     * sid 用户ID 
     */
    @Id
    @Column(name = "sid")
    private String sid;
	/**
	 * 手机号
	 */
	@Column(name = "mobile")
	private String mobile;
    。。。。。。


}
@Entity
@Data
@Table(name = "address")
@EntityListeners(AuditingEntityListener.class)
public class UserAddress implements Serializable {
    
    @Id
    @Column(name = "address_id")
    private String addressId;
    @Column(name = "street")
    private String street;
    @Column(name = "sid")
    private String sid;
    。。。。。。。。。

}

3 开始配置文件 application.properties  通过分片类实现分6个库单表(如果需要多表,自行修改配置即可满足)

server.port=8072
#jpa配置
spring.jpa.hibernate.ddl-auto=none
spring.jpa.database=mysql
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect
#datasource-name
spring.shardingsphere.datasource.names=ds0,ds1,ds2,ds3,ds4,ds5
#配置数据源
spring.shardingsphere.datasource.ds0.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds0.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds0.url=jdbc:mysql://localhost:3306/ds0?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&maxReconnects=15000&allowMultiQueries=true&useSSL=false
spring.shardingsphere.datasource.ds0.username=root
spring.shardingsphere.datasource.ds0.password=

spring.shardingsphere.datasource.ds1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds1.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds1.url=jdbc:mysql://localhost:3306/ds1?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&maxReconnects=15000&allowMultiQueries=true&useSSL=false
spring.shardingsphere.datasource.ds1.username=root
spring.shardingsphere.datasource.ds1.password=

spring.shardingsphere.datasource.ds2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds2.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds2.url=jdbc:mysql://localhost:3306/ds2?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&maxReconnects=15000&allowMultiQueries=true&useSSL=false
spring.shardingsphere.datasource.ds2.username=root
spring.shardingsphere.datasource.ds2.password=


spring.shardingsphere.datasource.ds3.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds3.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds3.url=jdbc:mysql://localhost:3306/ds3?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&maxReconnects=15000&allowMultiQueries=true&useSSL=false
spring.shardingsphere.datasource.ds3.username=root
spring.shardingsphere.datasource.ds3.password=

spring.shardingsphere.datasource.ds4.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds4.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds4.url=jdbc:mysql://localhost:3306/ds4?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&maxReconnects=15000&allowMultiQueries=true&useSSL=false
spring.shardingsphere.datasource.ds4.username=root
spring.shardingsphere.datasource.ds4.password=

spring.shardingsphere.datasource.ds5.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.ds5.driver-class-name=com.mysql.jdbc.Driver
spring.shardingsphere.datasource.ds5.url=jdbc:mysql://localhost:3306/ds5?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&failOverReadOnly=false&maxReconnects=15000&allowMultiQueries=true&useSSL=false
spring.shardingsphere.datasource.ds5.username=root
spring.shardingsphere.datasource.ds5.password=


####   分库策略  多库单表 ######
##### 数据节点 #####
##用户信息1sys_user
spring.shardingsphere.sharding.tables.sys_user.actual-data-nodes=ds$->{0..5}.sys_user
##用户地址2address
spring.shardingsphere.sharding.tables.address.actual-data-nodes=ds$->{0..5}.address


#分片场景
#1sys_user分片列名称
spring.shardingsphere.sharding.tables.sys_user.database-strategy.standard.sharding-column=sid
#分片算法,由于使用了uuid 的 sid使用groovy的表达式不能满足,需要自己实现PreciseShardingAlgorithm接口并提供无参数的构造器
spring.shardingsphere.sharding.tables.sys_user.database-strategy.standard.precise-algorithm-class-name=com.sharding.dbtablejpa.config.DataBasesPreciseShardingAlgorithm
#2addresses分片列名称
spring.shardingsphere.sharding.tables.address.database-strategy.standard.sharding-column=sid
#分片算法,由于使用了uuid 的 sid使用groovy的表达式不能满足,需要自己实现PreciseShardingAlgorithm接口并提供无参数的构造器
spring.shardingsphere.sharding.tables.address.database-strategy.standard.precise-algorithm-class-name=com.sharding.dbtablejpa.config.DataBasesPreciseShardingAlgorithm

4 uuid的分片字段sid 类的实现

public class DataBasesPreciseShardingAlgorithm implements PreciseShardingAlgorithm {
 
    /**
     *
     * @param collection  库名集合
     * @param preciseShardingValue 分片列
     * @return
     */
    @Override
    public String doSharding(Collection collection, PreciseShardingValue preciseShardingValue) {
        // 分片字段值
        String value = preciseShardingValue.getValue();
      

        return "ds"+TabSuffix.getTabSuffixCurrent(6,value);
    }
}
public class TabSuffix {

	/**
	 * 根据UUID首字母的ASC码获取表.
	 * UUID首字母包含[0123456789abcdefghijklmnopqrstuvwxyz]
	 * 分表个数必须被36整除或整除36(1 2 3 4 6 9 12 18 36)
	 * @return 结果如"1"
	 */
	public static String getTabSuffixCurrent(int splitdemision, String uuidStr){
		System.out.println("uuidStr"+uuidStr);
		System.out.println("splitdemision"+splitdemision);
		// 无法被36整除,异常返回-1
		if(36 % splitdemision != 0){
			return "-1";
		}
		
		// 获取首字母asc值(第一种)
		int asc = getAsc(uuidStr.substring(0,1));
		// 获取字母的hashCode(第二种)
		// int asc = uuidStr.substring(0,1).hashCode();
		if(splitdemision == 1 || splitdemision == 2 || splitdemision == 3 
				|| splitdemision == 4 || splitdemision == 12){
			if(asc == 121 || asc == 122){
				asc += 9;
			}
		}else if(splitdemision == 9){
			if(asc == 120){
				asc += 3;
			}
		}else if(splitdemision == 18){
			if(asc == 120 || asc == 121 || asc == 122){
				asc -= 8;
			}
		}else if(splitdemision == 36){
			if(asc == 120 || asc == 121 || asc == 122){
				asc += 10;
			}
		}
		int tableIdx = asc % splitdemision + 1;
		return String.valueOf(tableIdx-1);
	}
	
	/**
     * 字符转ASC
     * 
     * @param st
     * @return
     */
    public static int getAsc(String st) {
        byte[] gc = st.getBytes();
        return (int) gc[0];
    }
	
	
	public static void main(String[] args) {
		// 初始UUID首字母
		String str ="2,8,b,b,8,2,1";
		String[] strGro = str.split(",");
		// 表下标:1 2 3 4 9 12 18 36
		int splitdemision = 6;
		TreeMap map = new TreeMap<>();
		for (String item : strGro) {
			item += "uuid";
			int value = Integer.parseInt(getTabSuffixCurrent(splitdemision, item));
			if(map.containsKey(value)){
				map.put(value, map.get(value)+1);
			}else{
				map.put(value, 1);
			}
		}

		// 测试打印数据
		for (Entry entry : map.entrySet()) {
			System.out.println(entry.getKey()+"--"+entry.getValue());
		}
	}
}

此算法引用自 https://blog.csdn.net/hanshimeng/article/details/88915573 谢谢。

5 测试类省略。自行编写user的增加和address地址的增加,看为uuid 的sid user 是否和address是否落在一个数据库。同样查询删除。

6 不需要分表的配置如下

### 不需要分表的 配置####
#bc配置表1config
spring.shardingsphere.sharding.tables.config_info.actual-data-nodes=ds0.config_info
##配置不分表默认数据源和表
spring.shardingsphere.sharding.default-data-source-name=ds0
spring.shardingsphere.sharding.binding-tables=config_info

 

你可能感兴趣的:(JAVA,数据库,mysql,中间件,分布式)