赞
踩
在前面进行了基本的Shardingsphere之后,在一些其他的复杂条件下,可以使用自定义精确分片算法(PreciseShardingAlgorithm),通常用来处理=或者in条件的情况比较多。在该demo中,通过user_id来分库,公司Id(company_id)来分表,实现精确的不同分库分表。
CREATE TABLE `b_order0` ( `id` bigint NOT NULL AUTO_INCREMENT, `is_del` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否被删除', `company_id` int NOT NULL COMMENT '公司ID', `position_id` bigint NOT NULL COMMENT '职位ID', `user_id` int NOT NULL COMMENT '用户id', `publish_user_id` int NOT NULL COMMENT '职位发布者id', `resume_type` int NOT NULL DEFAULT '0' COMMENT '简历类型:0 附件 1 在线', `status` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '投递状态 投递状态 WAIT-待处理 AUTO_FILTER-自动过滤 PREPARE_CONTACT-待沟通 REFUSE-拒绝 ARRANGE_INTERVIEW-通知面试', `create_time` datetime NOT NULL COMMENT '创建时间', `update_time` datetime DEFAULT NULL COMMENT '操作时间', `work_year` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '工作年限', `name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '投递简历人名字', `position_name` varchar(256) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '职位名称', `resume_id` int DEFAULT NULL COMMENT '投递的简历id(在线和附件都记录,通过resumeType进行区别在线还是附件)', PRIMARY KEY (`id`), KEY `i_comId_pub_ctime` (`company_id`,`publish_user_id`,`create_time`) USING BTREE, KEY `index_companyId_positionId` (`company_id`,`position_id`) USING BTREE, KEY `index_companyId_status` (`company_id`,`status`(255),`is_del`) USING BTREE, KEY `index_createTime` (`create_time`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=983779921828511746 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
数据源和表截图:2个数据源,4个表
其他配置见该栏前一篇文章:配置信息
spring: sharding-sphere: datasource: names: ftdb0,ftdb1 ftdb0: type: com.zaxxer.hikari.HikariDataSource jdbc-url: jdbc:mysql://localhost:3306/ftdb0?useUnicode=true&autoReconnect=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123mysql ftdb1: type: com.zaxxer.hikari.HikariDataSource jdbc-url: jdbc:mysql://localhost:3306/ftdb1?useUnicode=true&autoReconnect=true&characterEncoding=UTF-8&allowMultiQueries=true&useSSL=false driver-class-name: com.mysql.cj.jdbc.Driver username: root password: 123mysql sharding: tables: b_order: actualDataNodes: ftdb${0..1}.b_order${0..1} database-strategy: # 数据库精确精确分库策略 standard: #这个地方不可忽略 sharding-column: user_id precise-algorithm-class-name: com.example.ftserver.sharding.shardingalgrithm.precise.FtDataBasePreciseShardingAlgorithm key-generator: column: id type: SNOWFLAKE tableStrategy: #表精确分片策略 standard: shardingColumn: company_id preciseAlgorithmClassName: com.example.ftserver.sharding.shardingalgrithm.precise.FtTablePreciseShardingAlgorithm
主配置文件application.yml需要引入自定义的分片配置:
在该demo中, 需要根据用户Id(user_id%2)取模来分库,尾数为奇数的去到数据库:ftdb1,尾数为偶数的去数据库:ftdb0;且还需要根据公司Id(company_id%2)取模,尾数为奇数的去到b_order1,尾数为偶数的去b_order0。其他的自定义算法可以根据自身实际情况重新编写
数据库分片算法示例:
package com.example.ftserver.sharding.shardingalgrithm.precise; import lombok.extern.slf4j.Slf4j; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue; import org.springframework.stereotype.Component; import java.util.Collection; /** * @author: zyh * @description: 自定义精确分片算法测试:数据库分片,根据userId精确分片 * */ @Component @Slf4j public class FtDataBasePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Integer> { @Override public String doSharding(Collection<String> collection, PreciseShardingValue<Integer> preciseShardingValue) { // 获取分片键的值 Integer userId = preciseShardingValue.getValue(); String logicTableName = preciseShardingValue.getLogicTableName(); log.info("分片的userId: {},逻辑表名 logicTableName: {}",userId,logicTableName); // 根据userId对数据库进行了分片,这里可以根据实际情况进行修改 String dataSourceName = "ftds" + (userId % 2); // 我们有两个数据源,这里取模得到分片结果 log.info("分片结果 dataSourceName: {}",dataSourceName); // 返回对应的数据源名称 for (String name : collection) { if (name.endsWith(String.valueOf(userId % 2))) { return name; } } return dataSourceName; } }
精确分表示例代码:
package com.example.ftserver.sharding.shardingalgrithm.precise; import lombok.extern.slf4j.Slf4j; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingAlgorithm; import org.apache.shardingsphere.api.sharding.standard.PreciseShardingValue; import org.springframework.stereotype.Component; import java.util.Collection; import java.util.Objects; /** * @author: zyh * @description: 自定义精确分片算法测试:表分片,根据company_id精确分片 * */ @Component @Slf4j public class FtTablePreciseShardingAlgorithm implements PreciseShardingAlgorithm<Long> { @Override public String doSharding(Collection<String> collection, PreciseShardingValue<Long> preciseShardingValue) { // 获取分片的公司Id long companyId = 0L; if (Objects.nonNull(preciseShardingValue.getValue())) { if (preciseShardingValue.getValue() instanceof Long) { companyId = preciseShardingValue.getValue(); } else { // 不知道为何返回的是Integer // 这里需要转换成Long,否则会有异常 String value = String.valueOf(preciseShardingValue.getValue()); companyId=Long.parseLong(value); } } // 模拟根据分片键的值取模,可以使用其他算法 long result = companyId % 2; // 获取逻辑表名 String logicTableName = preciseShardingValue.getLogicTableName(); // 实际表名 String physicalTableName = logicTableName + result; log.info("分片键的值:{},物理表名:{}", companyId, physicalTableName); if (collection.contains(physicalTableName)) { return physicalTableName; } throw new UnsupportedOperationException("未找到对应的分表信息,表:"+logicTableName+",分片键的值:"+companyId); } }
package com.example.ftserver; import cn.hutool.core.util.RandomUtil; import com.example.ftserver.mapper.BOrderMapper; import com.test.ft.common.entity.BOrderEntity; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.Repeat; import org.springframework.test.context.junit4.SpringRunner; @RunWith(SpringRunner.class) @SpringBootTest() public class CustomPreciseShardingTest { @Autowired private BOrderMapper mapper; @Test @Repeat(4) // SpringBoot提供的重复执行注解,该配置相当于重复执行4次 public void testCustomPreciseSharding(){ BOrderEntity entity = new BOrderEntity(); entity.setIsDel(false); // Hutool提供的随机数工具类 entity.setCompanyId(RandomUtil.randomInt(100,200)); entity.setPositionId(RandomUtil.randomLong(5,10)); entity.setUserId(RandomUtil.randomInt(2,5)); entity.setPublishUserId(101); entity.setResumeType(1); entity.setStatus("Auto"); entity.setWorkYear(String.valueOf(RandomUtil.randomInt(2))); entity.setName("数据库运维工程师"); entity.setPositionName("数据库运维工程师"); entity.setResumeId(RandomUtil.randomInt(5)); int insert = mapper.insert(entity); System.out.println("insert = "+ insert); } }
可以看到对应的数据已经根据设置的分片插入了不同的数据库
数据库数据:
1.根据Id查询时
可以看到,在每个库,每个表里面都做了一次查询,这是因为b_order表的Id没有作为分片键,走了全路由查询
2.使用库或者表分片键作为精确查询条件时:
使用表分片键查询:
使用库分片键查询时:
可以看到在使用库或者表分片键作为精确查询条件,只会执行该分片键相对应的查询路径
3.使用分库和分表的分片键查询:
可以看到SQL查询,走了全部的分库分表的路径查询
结论: 由此测试可以得到结论,在执行了精确分库分表算法之后,在查询之前,最好根据对应的分库分表算法,对查询的参数根据算法预处理和封装,以实现最佳的查询效果
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。