shardingSphere的comple分片策略算法实践及遇到的问题

1,549 阅读4分钟

微信技术群:Day9884125

1 complex复合分片算法

1.1 application.properties文件

# 配shardingsphere虚拟数据库,多库操作配置
spring.shardingsphere.datasource.names=database1, database2
# 配database1的数据源
spring.shardingsphere.datasource.database1.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.database1.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.database1.url=jdbc:mysql://8.142.93.199:3306/manage?useUnicode=true&characterEncoding=UTF-8
spring.shardingsphere.datasource.database1.username=root
spring.shardingsphere.datasource.database1.password=Duay4125!

# 配database2的数据源
spring.shardingsphere.datasource.database2.type=com.alibaba.druid.pool.DruidDataSource
spring.shardingsphere.datasource.database2.driver-class-name=com.mysql.cj.jdbc.Driver
spring.shardingsphere.datasource.database2.url=jdbc:mysql://123.56.5.188:3306/managecopy?useUnicode=true&characterEncoding=UTF-8
spring.shardingsphere.datasource.database2.username=root
spring.shardingsphere.datasource.database2.password=Lhl12345!

# 配置逻辑表student,actual-data-nodes代表真实表分布,代表database1和database2库里面的student1和student2表
# 前提数据库必须有student1和student2这两张表,他不会自己创建,否则会报错
spring.shardingsphere.sharding.tables.student.actual-data-nodes=database$->{1..2}.student_$->{1..2}

# 数据库中表的配置
# 数据库中表的student主键是id
spring.shardingsphere.sharding.tables.student.key-generator.column=id
# 主键生成策略是雪花算法
spring.shardingsphere.sharding.tables.student.key-generator.type=SNOWFLAKE
# worker.id是雪花算法里面需要的参数,这个参数是可选的,我们建议配上
spring.shardingsphere.sharding.tables.student.key-generator.props.worker.id=1

# 按照多个字段查询的配置方式
spring.shardingsphere.sharding.tables.student.table-strategy.complex.sharding-columns=id,tearchid
# 这需要自己定义一个算法类,范围查询配置类
spring.shardingsphere.sharding.tables.student.table-strategy.complex.algorithm-class-name=com.example.sharding.shardingsphere.algorithm.OneComplexKeysShardingAlgorithm
        
# 数据库中库的配置
# 配分库分表的表策略,sharding-column是分片的键
spring.shardingsphere.sharding.tables.student.database-strategy.complex.sharding-columns=id,tearchid
# 范围查询的库配置
spring.shardingsphere.sharding.tables.student.database-strategy.complex.algorithm-class-name=com.example.sharding.shardingsphere.algorithm.OneDBComplexKeysShardingAlogorithm
        
# 打印实时日志
spring.shardingsphere.props.sql.show=true

1.2 实体类

package com.example.sharding.shardingsphere.entity;

import lombok.Data;

/**
* 作者: duay
* 时间:2022/06/09
* 描述:shardingjdbc实体类
*/
@Data
public class Student {
    /** 主键id
    * org.springframework.dao.DuplicateKeyException:
    * Error updating database.  Cause: java.sql.SQLIntegrityConstraintViolationException:
    * Duplicate entry '0' for key 'PRIMARY'
    *
    * 当类型是 int和不是包装类long时,shardingjdbc的雪花算法不会起效,会报上面的错
    * 主键id是Long,数据库的id可以是varchar类型
    */
    private Long id;
    /** 名字 */
    private String name;
    /** 老师id */
    private int tearchid;
    /** 状态 */
    private String status;
}

1.3 mapper接口

package com.example.sharding.shardingsphere.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.sharding.shardingsphere.entity.Student;
/**
* 作者: duay
* 时间:2022/06/09
* 描述:继承mybatisplus提供的BaseMapper,这样就不用写crud了
*/
public interface StudentMapper extends BaseMapper<Student> {
    
}

1.4 启动函数

package com.example.sharding.shardingsphere;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
//用于扫描mapper接口类
@MapperScan("com.example.sharding.shardingsphere.mapper")
public class ShardingsphereApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(ShardingsphereApplication.class, args);
    }
    
}

1.5 分片规则类

1.5.1 表分片规则

package com.example.sharding.shardingsphere.algorithm;

import com.google.common.collect.Range;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingValue;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

/**
* 作者:duay
* 时间:2022/06/09
* 描述:按照多个字段查询的算法
*/
public class OneComplexKeysShardingAlgorithm implements ComplexKeysShardingAlgorithm<Long> {
    /**
    * @param collection: 目标数据源或者表的值
    * @param complexKeysShardingValue: logicTableName逻辑表名,
    * columnNameAndShardingValuesMap分片列的精确值集合
    * columnNameAndRangeValuesMap分片列的范围值集合
    * @return
    */
    @Override
    public Collection<String> doSharding(Collection<String> collection, ComplexKeysShardingValue<Long> complexKeysShardingValue) {
        // 实现between进行范围分片
        // 例如 select * from student where id in (1, 3, 5) and tearchid between 200 and 300;
        Collection<Long> id = complexKeysShardingValue.getColumnNameAndShardingValuesMap().get("id");
        Range<Long> range = complexKeysShardingValue.getColumnNameAndRangeValuesMap().get("tearchid");
        
        List<String> result = new ArrayList<>();
        Long lowerEndpoint = range.lowerEndpoint();//200
        Long upperEndpoint = range.upperEndpoint();//300
        //实现自定义分片逻辑 例如可以自己实现student_$->{id%2+1 + (30-20)+1}这样的分片逻辑
        for(Long id2 : id){
            BigInteger idResult = BigInteger.valueOf(id2);
            BigInteger target = (idResult.mod(BigInteger.valueOf(2L))).add(new BigInteger("1"));
            result.add("student_" + target);
        }
        return result;
    }
}

1.5.2 库分片规则

package com.example.sharding.shardingsphere.algorithm;

import com.google.common.collect.Range;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.complex.ComplexKeysShardingValue;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm;
import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * 描述: 自定义分库规则类
 * 作者: duay
 * 时间: 2022/06/12
 */
public class OneDBComplexKeysShardingAlogorithm implements ComplexKeysShardingAlgorithm<Long> {
    /**
     * @param collection: 所有的数据源
     * @param complexKeysShardingValue: sql执行时传入的分片值
     * @return: 返回
     */
    @Override
    public Collection<String> doSharding(Collection<String> collection, ComplexKeysShardingValue<Long> complexKeysShardingValue) {
        Collection<Long> ids = complexKeysShardingValue.getColumnNameAndShardingValuesMap().get("id");
        //Range<Long> ids = complexKeysShardingValue.getColumnNameAndRangeValuesMap().get("id");
        List<String> result = new ArrayList<>();
        for(Long id : ids){
            BigInteger idResult = BigInteger.valueOf(id);
            System.out.println("id{}" + id);
            BigInteger index = (idResult.mod(BigInteger.valueOf(2L))).add(new BigInteger("1"));
            System.out.println("目标数据库{}" + "database" + index);
            result.add("database" + index);
        }
        return result;
    }
}

1.6 测试类

package com.example.sharding.shardingsphere;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.sharding.shardingsphere.entity.Student;
import com.example.sharding.shardingsphere.mapper.StudentMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import javax.annotation.Resource;

@SpringBootTest
@RunWith(SpringRunner.class)
public class ShardingTest {
    @Resource
    private StudentMapper studentMapper;

    @Test
    public void addStudent(){
        for(int i=0; i<10; i++){
            Student s = new Student();
            //主键就不需要了,因为主键是雪花算法
            //sdto.setId(1);
            s.setName("张三");
            s.setStatus("1");
            s.setTearchid(10);
            studentMapper.insert(s);
        }
    }

    @Test
    public void queryStudent(){
        QueryWrapper result = new QueryWrapper<Student>();
        // 精确查询
        //result.eq("id", 1535492214268665858L);
        
        result.in("id", 1535492214268665858L, 1535492218039345154L);
        result.between("tearchid", 1L, 6L);

        for(Object o : studentMapper.selectList(result)){
            System.out.println("查询结果:" + o);
        }
    }
}

1.7 总结

   1、首先测试类中只是用精确会报错,例如result.eq("id", 1535492214268665858L)。而使用result.in("id", 1535492214268665858L, 1535492218039345154L);和result.between("tearchid", 1L, 6L);可以正常使用。使用result.eq("id", 1535492214268665858L);和result.between("tearchid", 1L, 6L);也可以使用。
   2、再者需要注意的是,complex分片算法的实现类需要继承的接口都是同一个叫做ComplexKeysShardingAlgorithm,而standard分片算法是两个,这里主要注意一下。
   3、需要又特意注意,一般网上实现的都是complex算法的分表实现类。没有实现分库的。分库和分表的有一个区别。分库时候这个实现类需要注意以下几点。

/** 
 * 1、分库中获取的时候,注意complexKeysShardingValue参数中方法两个方法
 * getColumnNameAndShardingValuesMap()是用来获取精确查询的列名
 * getColumnNameAndRangeValuesMap()是用来获取范围查询的列名
 * 这两个方法获取时候不能乱用,必须针对测试类中的列名使用。如果对这个列名使用的精确查询,在
 * 算法中使用第一个方法获取。如果对列名值进行范围查询用第二个方法获取
 * 
 * 2、用来作为落库的列名,在查询的时候,个人建议:落库列名不要使用范围查询。如果这个列名
 * 使用范围查询,则getColumnNameAndShardingValuesMap()方法会报错找不到库
 */
Collection<Long> ids = complexKeysShardingValue.getColumnNameAndShardingValuesMap().get("id");
Range<Long> ids = complexKeysShardingValue.getColumnNameAndRangeValuesMap().get("id");