ShardingSphere复合分片之hash槽算法

news2025/4/16 12:10:50

前言

  上一篇《ShardingSphere复合分片》中有详细介绍多key多value的复合分片算法应该如何设计,在大部分情况下该算法是没有问题的,但是一旦涉及到数据迁移时,该算法的缺点就暴露无疑了。

为满足日益增长的用户或者订单的需求,在分库分表的同时,我们不可避免的会对数据库进行扩容,这时就涉及到数据迁移,但是上一篇最终采用的是求模取余的算法对数据库进行分片,该算法一旦需要增加或者删减数据库服务器的时候,几乎要使所有数据进行迁移,影响面特别广,且不好控制,容易出错。为解决这一弊端,我们可以借鉴redis中多主多从集群中的数据分片策略,即hash槽策略来优化我们mysql的分片策略。

hash槽算法

  首先该算法会分配16384|(2^14)个槽,通过hash函数,将数据均匀分布在这些槽,针对分片,我们只需要关注将这16384槽如何分配到具体的分片信息上。如果用户需要增加分片,那么我们只需要移动一部分槽到新的分片上就完成了数据的迁移,其他大部分槽是不需要移动的,这就避免了大规模的数据迁移。用户如果需要减少分片也类似。

将Hash槽算法引入到ShardingSphere

  在上一篇我们介绍到,通过生成分布式id,同时让这个分布式id记录所处的库以及所处的表,也就是记录所处分片,以完成整个分片逻辑。使用Hash槽算法也类似,在生成分布式订单id过程中,通过传入的relatedId进行hash运算后,计算得到的hash值应该处于哪个槽,再由具体的槽计算出应该划分到哪个分片,最后将分片信息保存到分布式id中。以上这样就完成了分片逻辑的计算,后续的ShardingSphere逻辑和上一篇一模一样,这里就不再冗余了。

  不过需要注意一点,由于数据迁移时,是根据槽信息来迁移的,所以在信息入库时我们也需要保存槽信息,也就是说我们生成的分布式id需要包含槽信息,以便数据迁移。

具体部分实现代码如下所示:

分布式订单id生成类:


/** 根据Hash槽生成分布式id,同时解决复合key的分库分表问题,以及在增删节点时,求模取余算法在分库分表时大规模的数据迁移问题。
 *
 *  该算法借鉴redis hash槽
 * @author chenjian
 * @version 1.0
 * @date 2025/04/15 10:17
 * @className HashSlotKeyGenerator
 * @desc 自定义分布式主键生成器
 */
@Component
public class HashSlotKeyGenerator implements KeyGenerator {
    @Autowired
    private AppProperties appProperties;

    @Resource(name = "hashSlotDefaultShardingStrategy")
    private HashSlotAllocationStrategy hashSlotAllocationStrategy;

    @Autowired
    private SequenceGenerator sequenceGenerator;

    @Override
    public String generateKey(DbAndTableEnum targetEnum, String relateId) {
        if (StringUtils.isBlank(relateId)) {
            throw new IllegalArgumentException("路由id参数为空");
        }

        Map<String,String> map = null;
        int slot = crc16Hash(relateId) % SLOT_COUNT;
        // 根据slot分配数据库节点

        map =  hashSlotAllocationStrategy.shardingBySlot(slot,Integer.parseInt(appProperties.getShardingNodes().getAmount()),DbAndTableEnum.T_ORDER);
        StringBuilder key = new StringBuilder();
        /* 总共37位 */
        /** 1.id业务前缀*/
        String idPrefix = targetEnum.getCharsPrefix();
        /** 2.id数据库索引位*/
        String dbIndex = map.get(ShardingConstant.DB_INDEX);
        /** 3.id表索引位*/
        String tbIndex = map.get(ShardingConstant.TB_INDEX);
        /** 4.id规则版本位*/
        String idVersion = targetEnum.getIdVersion();
        /** 5.id时间戳位*/
        String timeString = DateUtils.formatTime(new Date());
        /** 6.id分布式机器位 2位*/
        String distributedIndex = ApiUtils.getDistributedId(2);
        /** 7.随机数位*/
        String sequenceId = sequenceGenerator.getNextVal(targetEnum, Integer.parseInt(dbIndex), Integer.parseInt(tbIndex));
        /** 8.5位slot值,用于保存到数据库,方便数据迁移*/
        String hashSlot = StringUtil.fillZero(String.valueOf(slot), ShardingConstant.DB_SUFFIX_LENGTH);

        /** 库表索引靠前*/
        return key.append(idPrefix)
                .append(dbIndex)
                .append(tbIndex)
                .append(idVersion)
                .append(timeString)
                .append(distributedIndex)
                .append(sequenceId)
                .append(hashSlot).toString();
    }

    private int crc16Hash(String key) {
        int crc = 0xFFFF;
        int polynomial = 0x1021;
        for (byte b : key.getBytes()) {
            crc ^= (b & 0xFF);
            for (int i = 0; i < 8; i++) {
                if ((crc & 0x0001) != 0) {
                    crc = (crc >>> 1) ^ polynomial;
                } else {
                    crc = crc >>> 1;
                }
            }
        }
        return crc;
    }
}

根据槽信息获取具体分片

​

@Service("hashSlotDefaultShardingStrategy")
public class HashSlotDefaultShardingStrategy implements HashSlotAllocationStrategy{
    @Override
    public Map<String, String> shardingBySlot(int slot, int shardingAmount, DbAndTableEnum targetEnum) {
        if (slot < 0 || slot >= SLOT_COUNT) {
            throw new IllegalArgumentException("槽号超出范围");
        }

        int baseSlotsPerShard = SLOT_COUNT / shardingAmount;
        int remainingSlots = SLOT_COUNT % shardingAmount;

        int shardIndex;
        if (slot < remainingSlots * (baseSlotsPerShard + 1)) {
            shardIndex = slot / (baseSlotsPerShard + 1);
        } else {
            shardIndex = remainingSlots + (slot - remainingSlots * (baseSlotsPerShard + 1)) / baseSlotsPerShard;
        }

        Map<String,String> map = new HashMap<>(2);
        String preDbIndex = String.valueOf(StringUtil.getDbIndexByMod(shardIndex,targetEnum.getDbCount(),targetEnum.getTbCount()));
        String dbIndex = StringUtil.fillZero(preDbIndex, ShardingConstant.DB_SUFFIX_LENGTH);
        /** 获取表索引*/
        String preTbIndex = String
                .valueOf(StringUtil.getTbIndexByMod(shardIndex,targetEnum.getDbCount(),targetEnum.getTbCount()));
        String tbIndex = StringUtil
                .fillZero(preTbIndex,ShardingConstant.TABLE_SUFFIX_LENGTH);
        map.put(ShardingConstant.DB_INDEX, dbIndex);
        map.put(ShardingConstant.TB_INDEX, tbIndex);
        return map;
    }
}

​

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2335945.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Web三漏洞学习(其二:sql注入)

靶场&#xff1a;NSSCTF 、云曦历年考核题 二、sql注入 NSSCTF 【SWPUCTF 2021 新生赛】easy_sql 这题虽然之前做过&#xff0c;但为了学习sql&#xff0c;整理一下就再写一次 打开以后是杰哥的界面 注意到html网页标题的名称是 “参数是wllm” 那就传参数值试一试 首先判…

KrillinAI:视频跨语言传播的一站式AI解决方案

引言 在全球内容创作领域&#xff0c;跨语言传播一直是内容创作者面临的巨大挑战。传统的视频本地化流程繁琐&#xff0c;涉及多个环节和工具&#xff0c;不仅耗时耗力&#xff0c;还常常面临质量不稳定的问题。随着大语言模型(LLM)技术的迅猛发展&#xff0c;一款名为Krillin…

gravity`(控制 View 内部内容的对齐方式)

文章目录 **1. 常用取值****示例** **2. layout_gravity&#xff08;控制 View 在父容器中的对齐方式&#xff09;****常用取值****示例** **3. gravity vs layout_gravity 对比****4. 注意事项****5. 总结** 作用对象&#xff1a;当前 View 的内部内容&#xff08;如 TextView…

gitdiagram源码架构分析

https://github.com/ahmedkhaleel2004/gitdiagram 整体架构分析 前端请求入口&#xff1a; 后端对应接口&#xff1a; 后端调试 后端调试&#xff1a;会提示api_key失败的问题&#xff1a; 有两种方法解决&#xff1a; 1、注释掉下面的行代码&#xff1b; 方法二&#xff1…

蓝光三维扫描:汽车冲压模具与钣金件全尺寸检测的精准解决方案

随着汽车市场竞争日趋激烈&#xff0c;新车型开发周期缩短&#xff0c;安全性能要求提高&#xff0c;车身结构愈加复杂。白车身由多达上百个具有复杂空间型面的钣金件&#xff0c;通过一系列工装装配、焊接而成。 钣金件尺寸精度是白车身装配精度的基础。采用新拓三维XTOM蓝光…

深度学习占用大量内存空间解决办法

应该是缓存的问题&#xff0c;关机重启内存多了10G&#xff0c;暂时没找到别的方法 重启前 关机重启后

Matlab绘制函数方程图形

Matlab绘制函数方程图形&#xff1a; 多项式计算: polyval 函数 Values of Polynomials: polyval ( ) 绘制方程式图形&#xff1a; 代码如下&#xff1a; >> a[9,-5,3,7]; x-2:0.01:5; fpolyval(a,x); plot(x,f,LineWidth,2); xlabel(x); ylabel(f(x))…

电视盒子 刷armbian

参考 中兴电视盒子中兴B860AV3.2-M刷Armbian新手级教程-CSDN博客 1.刷安卓9 带root版本 a. 下载安卓线刷包 链接&#xff1a;https://pan.baidu.com/s/1hz87_ld2lJea0gYjeoHQ8A?pwdd7as 提取码&#xff1a;d7as b.拆机短接 3.安装usbburning工具 使用方法 &#xff0c;…

AI应用开发之扣子第一课-夸夸机器人

首先&#xff0c;进入官网&#xff1a;点击跳转至扣子。 1.创建智能体 登录进网站后&#xff0c;点击左上角&#xff0b;图标&#xff0c;创建智能体&#xff0c;输入智能体名称、功能介绍 2.输入智能体提示词 在“人设与回复逻辑”输入以下内容&#xff1a; # 角色 你是一…

【计算机网络实践】(十二)大学校园网综合项目设计

本系列包含&#xff1a; &#xff08;一&#xff09;以太网帧分析与网际互联协议报文结构分析 &#xff08;二&#xff09;地址解析协议分析与传输控制协议特性分析 &#xff08;三&#xff09;交换机的基本操作、配置、 虚拟局域网配置和应用 &#xff08;四&#xff09;交…

uniapp小程序位置授权弹框与隐私协议耦合(合而为一)(只在真机上有用,模拟器会分开弹 )

注意&#xff1a; 只在真机上有用&#xff0c;模拟器会分开弹 效果图&#xff1a; 模拟器效果图&#xff08;授权框跟隐私政策会分开弹&#xff0c;先弹隐私政策&#xff0c;同意再弹授权弹框&#xff09;&#xff1a; manifest-template.json配置&#xff08; "__usePr…

【星闪模组开发板WS8204SLEBLEModule】星闪数据收发测试

目录 开发板简介 串口设置 主从模式设置 AT命令数据发送 透传模式数据发送 结语 本文首发于《电子产品世界》论坛&#xff1a;【星闪模组开发板WS8204SLE&BLEModule】星闪数据收发测试-电子产品世界论坛https://forum.eepw.com.cn/thread/392011/1 感谢eepw论坛和成…

基础知识:Dify 错误排查

Case1:Dify 卡在管理员界面 查看容器状态 docker compose ps 可以看到有个容器异常:docker_db_1 的状态是 Restarting(表示一直在重启) 解决方案 参考:https://github.com/langgenius/dify/issues/5731

spring cloud微服务断路器详解及主流断路器框架对比

微服务断路器详解 1. 核心概念 定义&#xff1a;断路器模式通过快速失败机制防止故障扩散&#xff0c;当服务调用出现异常或超时时&#xff0c;自动切换到降级逻辑&#xff0c;避免级联故障。核心功能&#xff1a; 熔断&#xff1a;在故障阈值&#xff08;如错误率&#xff09…

(小白0基础) 微调deepseek-8b模型参数详解以及全流程——训练篇

​ 本篇参考bilibili如何在本地微调DeepSeek-R1-8b模型_哔哩哔哩_bilibili 上篇&#xff1a;(小白0基础) 租用AutoDL服务器进行deepseek-8b模型微调全流程(Xshell,XFTP) —— 准备篇 初始变量 max_seq_length 2048 dtype None load_in_4bit True单批次最大处理模型大小dy…

关于汽车辅助驾驶不同等级、技术对比、传感器差异及未来发展方向的详细分析

以下是关于汽车辅助驾驶不同等级、技术对比、传感器差异及未来发展方向的详细分析&#xff1a; 一、汽车辅助驾驶等级详解 根据SAE&#xff08;国际自动机工程师学会&#xff09;的标准&#xff0c;自动驾驶分为 L0到L5 六个等级&#xff1a; 1. L0&#xff08;无自动化&…

mongodb7日志特点介绍:日志分类、级别、关键字段(下)

#作者&#xff1a;任少近 上篇《mongodb7日志特点介绍&#xff1a;日志分类、级别、关键字段(上)》 链接: link 文章目录 4.日志会输出F/E/W/I四种情况5.日志关键字段6.日志量验证情况7.总结 4.日志会输出F/E/W/I四种情况 在MongoDB7中&#xff0c;日志输出按照严重性分为四种…

word中插入图片显示不完整,怎么处理让其显示完整?

在WORD里插入图片后&#xff0c;选择嵌入式发现插入的图片显示不正常&#xff0c;只能显示底部一部分&#xff0c;或者遮住文字。出现此故障的原因有可能是设置为固定值的文档行距小于图形的高度&#xff0c;从而导致插入的图形只显示出了一部分。 1.选中图片&#xff0c;然后点…

SAP S4HANA embedded analytics

SAP S4HANA embedded analytics

JavaWeb开发 Servlet底层 Servlet 过滤器 过滤器和拦截器 手写一个限制访问路径的拦截器

目录 万能图 过滤器自我理解 案例 实现Filter 接口 配置文件 web.xml 将过滤器映射到 servlet 用处 拦截器 手写案例 重写 preHandle() 方法 拦截处理 重写 postHandle() 方法 后处理 重写 afterHandle() 方法 完成处理 代码 如何配置拦截器 万能图 还是看一下这张…