文章目录

  • 前言
  • 一、一致性Hash是什么?
  • 二、使用步骤
    • 1.一致性hash算法
      • ConsistentHashAlgorithm
    • 2.初始化表结点,并映射到hash环
      • InitTableNodesToHashLoop
    • 3.创建分表算法
      • ConsistentShardingAlgorithm
    • 4.更改配置
  • 总结

前言

前几篇文章主要介绍了Springboot+Sharding-JDBC在分库分表中的实践,那么在实际场景中,我们可能会有需求对已经分表的表节点进行扩容。那么在分表算法为求余的情况下,如果增加一个节点,会导致大部分已存在的数据多要进行迁移,工程量巨大。
那除了求余分表算法外,还有其他算法能在缩扩容场景下更好的工作吗?!接下来我们一起看下一致性Hash算法在分库分表中的应用。

一、一致性Hash是什么?

一致性哈希算法在1997年由麻省理工学院提出,是一种特殊的哈希算法,目的是解决分布式缓存的问题。 在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系。一致性哈希解决了简单哈希算法在分布式哈希表( Distributed Hash Table,DHT) 中存在的动态伸缩等问题

一致性哈希算法将整个哈希值空间映射成一个虚拟的圆环,整个哈希空间的取值范围为0—2322^{32}232-1。整个空间按顺时针方向组织。0—2322^{32}232在零点中方向重合。接下来使用Hash算法对服务请求进行映射,将服务请求使用哈希算法算出对应的hash值,然后根据hash值的位置沿圆环顺时针查找,第一台遇到的服务器就是所对应的处理请求服务器。当增加一台新的服务器,受影响的数据仅仅是新添加的服务器到其环空间中前一台的服务器(也就是顺着逆时针方向遇到的第一台服务器)之间的数据,其他都不会受到影响。综上所述,一致性哈希算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性;

那么在分表应用中,如下图,我们先对table1、table2、table3、table4进行hash计算得到key1、key2、key3、key4并映射到范围为0—2322^{32}232-1的环中;加入此时我们是通过id进行分表操作,那么在存储时,我们先对id进行hash计算得出hahs值(hash(id1)),得到的值沿圆环顺时针查找遇到的第一个表节点,即是数据存储的真实表结点;接下来我们看下在Sharding-jdbc中的实际应用

二、使用步骤

1.一致性hash算法

ConsistentHashAlgorithm

public class ConsistentHashAlgorithm {//虚拟节点,key表示虚拟节点的hash值,value表示虚拟节点的名称@Getterprivate SortedMap<Long, String> virtualNodes = new TreeMap<>();//当节点的数目很少时,容易造成数据的分布不均,所以增加虚拟节点来平均数据分布//虚拟节点的数目;虚拟节点的生成主要是用来让数据尽量均匀分布//虚拟节点是真实节点的不同映射而已//比如真实节点user1的hash值为100,那么我们增加3个虚拟节点user1-1、user1-2、user1-3,分别计算出来的hash值可能就为200,345,500;通过这种方式来将节点分布均匀private static final int VIRTUAL_NODES = 3;public ConsistentHashAlgorithm() {}public ConsistentHashAlgorithm(SortedMap<Long, String> virtualTableNodes, Collection<String> tableNodes) {if (Objects.isNull(virtualTableNodes)) {virtualTableNodes = initNodesToHashLoop(tableNodes);}this.virtualNodes = virtualTableNodes;}public SortedMap<Long, String> initNodesToHashLoop(Collection<String> tableNodes) {SortedMap<Long, String> virtualTableNodes = new TreeMap<>();for (String node : tableNodes) {for (int i = 0; i < VIRTUAL_NODES; i++) {String s = String.valueOf(i);String virtualNodeName = node + "-VN" + s;long hash = getHash(virtualNodeName);virtualTableNodes.put(hash, virtualNodeName);}}return virtualTableNodes;}/*** 通过计算key的hash* 计算映射的表节点** @param key* @return*/public String getTableNode(String key) {String virtualNode = getVirtualTableNode(key);//虚拟节点名称截取后获取真实节点if (StringUtils.isNotBlank(virtualNode)) {return virtualNode.substring(0, virtualNode.indexOf("-"));}return null;}/*** 获取虚拟节点* @param key* @return*/public String getVirtualTableNode(String key) {long hash = getHash(key);// 得到大于该Hash值的所有MapSortedMap<Long, String> subMap = virtualNodes.tailMap(hash);String virtualNode;if (subMap.isEmpty()) {//如果没有比该key的hash值大的,则从第一个node开始Long i = virtualNodes.firstKey();//返回对应的服务器virtualNode = virtualNodes.get(i);} else {//第一个Key就是顺时针过去离node最近的那个结点Long i = subMap.firstKey();//返回对应的服务器virtualNode = subMap.get(i);}return virtualNode;}/*** 使用FNV1_32_HASH算法计算key的Hash值** @param key* @return*/public long getHash(String key) {final int p = 16777619;int hash = (int) 2166136261L;for (int i = 0; i < key.length(); i++)hash = (hash ^ key.charAt(i)) * p;hash += hash << 13;hash ^= hash >> 7;hash += hash << 3;hash ^= hash >> 17;hash += hash << 5;// 如果算出来的值为负数则取其绝对值if (hash < 0)hash = Math.abs(hash);return hash;}}

2.初始化表结点,并映射到hash环

该步骤会在应用启动时初始化分表的表结点,提前进行hash计算

InitTableNodesToHashLoop

public class InitTableNodesToHashLoop {@Resourceprivate ShardingDataSource shardingDataSource;@Getterprivate HashMap<String, SortedMap<Long, String>> tableVirtualNodes = new HashMap<>();@PostConstructpublic void init() {try {ShardingRule rule = shardingDataSource.getRuntimeContext().getRule();Collection<TableRule> tableRules = rule.getTableRules();ConsistentHashAlgorithm consistentHashAlgorithm = new ConsistentHashAlgorithm();for (TableRule tableRule : tableRules) {String logicTable = tableRule.getLogicTable();tableVirtualNodes.put(logicTable,consistentHashAlgorithm.initNodesToHashLoop(tableRule.getActualDataNodes().stream().map(DataNode::getTableName).collect(Collectors.toList())));}} catch (Exception e) {log.error("分表节点初始化失败 {}", e);}}
}

3.创建分表算法

ConsistentShardingAlgorithm

public class ConsistentShardingAlgorithmimplements PreciseShardingAlgorithm<Long>, RangeShardingAlgorithm<Long> {/*** 精确分片* 一致性hash算法*/@Overridepublic String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {//获取已经初始化的分表节点InitTableNodesToHashLoop initTableNodesToHashLoop =SpringContextUtils.getBean(InitTableNodesToHashLoop.class);if (CollectionUtils.isEmpty(availableTargetNames)) {return shardingValue.getLogicTableName();}//这里主要为了兼容当联表查询时,如果两个表非关联表则//当对副表分表时shardingValue这里传递进来的依然是主表的名称,//但availableTargetNames中确是副表名称,所有这里要从availableTargetNames中匹配真实表ArrayList<String> availableTargetNameList = new ArrayList<>(availableTargetNames);String logicTableName = availableTargetNameList.get(0).replaceAll("[^(a-zA-Z_)]", "");SortedMap<Long, String> tableHashNode =initTableNodesToHashLoop.getTableVirtualNodes().get(logicTableName);ConsistentHashAlgorithm consistentHashAlgorithm = new ConsistentHashAlgorithm(tableHashNode,availableTargetNames);return consistentHashAlgorithm.getTableNode(String.valueOf(shardingValue.getValue()));}/*** 范围查询规则* 可以根据实际场景进行修改* Sharding.** @param availableTargetNames available data sources or tables's names* @param shardingValue        sharding value* @return sharding results for data sources or tables's names*/@Overridepublic Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {return availableTargetNames;}
}

4.更改配置

tables:t_user:  #t_user表key-generator-column-name: id  #主键actual-data-nodes: ds0.t_user${0..39}    #数据节点,均匀分布table-strategy:  #分表策略使用一致性hash算法standard:sharding-column: idprecise-algorithm-class-name: com.none.sharding.infrastruc.shardingAlgorithm.ConsistentShardingAlgorithm

总结

以上即是一致性hash算法在分表分库中的实际应用;虽然一致性hash算法能在节点伸缩的时候尽量减少数据的迁移,但是当虚拟节点数量很多时依然会造成不少数据迁移,所以前期进行规划时一定要考虑虚拟节点的倍数设置。

Demo地址:Github

Springboot+Sharding-JDBC分库分表实践四之一致性Hash算法相关推荐

  1. Spring boot + Sharding JDBC 分库分表 及 分布式事务处理

    Sharding JDBC 基础概念 Apache ShardingSphere 是一套开源的分布式数据库解决方案组成的生态圈,它由 JDBC.Proxy 和 Sidecar(规划中)这 3 款既能够 ...

  2. Day 4 - PB级规模数据的Elasticsearch分库分表实践

    Day 4 - PB级规模数据的Elasticsearch分库分表实践 从2018年7月在开始在某阿里云数据中心部署Elasticsearch软件,到2018年12月共创建了15个集群,服务于客户的文 ...

  3. MariaDB Spider 数据库分库分表实践 分库分表

    分库分表 一般来说,数据库分库分表,有以下做法: 按哈希分片:根据一条数据的标识计算哈希值,将其分配到特定的数据库引擎中: 按范围分片:根据一条数据的标识(一般是值),将其分配到特定的数据库引擎中: ...

  4. SSM项目引入sharding JDBC进行分表

    SSM项目引入sharding JDBC进行分表 注意点: 本次集成sharing-jdbc 4.1.1,由于各个版本差别比较大,配置方式差别也特别大,请根据官方文档进行配置! 官方配置路径:http ...

  5. 大众点评订单分库分表实践之路

    http://dbaplus.cn/news-10-264-1.html 本文是关于大众点评订单分库分表实践的一个具体分享,包含对订单库的具体切分策略,以及我个人的一些思考. 背景 订单单表早已突破两 ...

  6. 也许是东半球直接底气的分库分表实践了

    点击上方"方志朋",选择"设为星标" 做积极的人,而不是积极废人 背景 前不久发过两篇关于分表的文章: 一次分表踩坑实践的探讨 分表后需要注意的二三事 从标题可 ...

  7. MySQL 分库分表实践

    文章目录 一.为什么要分库分表 二.库表太大产生的问题 三.垂直拆分 1. 垂直分库 2. 垂直分表 四.水平分表 1. 配置水平分表 2. 测试水平分表 一.为什么要分库分表 数据库架构演变 刚开始 ...

  8. SpringBoot整合Sharding-JDBC分库分表中间件

    1.引入依赖 主要引入Sharding-JDBC依赖sharding-jdbc-spring-boot-starter,其他的依赖都是为了后面的测试环节所准备的 pom.xml <!-- sha ...

  9. Mycat分库分表实践

    一.先了解基本概念 1.mycat是一款操作数据库的中间件,可以达到分库分表的效果,可以从分库分表中查询所有的数据,也可以将数据以一定的规则(rule.xml)写入分库和分表. 2.分库可以在一台服务 ...

最新文章

  1. 数据智能与计算机图形学领域推荐论文列表
  2. Alpha冲刺Day2
  3. poj-1845 Sumdiv nyoj - 928 小M的因子和
  4. 习题11-7 奇数值结点链表 (20 分) -链表
  5. 测试插件-infinitest介绍
  6. java web 应用技术与案例教程_《Java Web应用开发技术与案例教程》怎么样_目录_pdf在线阅读 - 课课家教育...
  7. 深度解析 | 用 AI 帮助听障人群,一共需要几步?
  8. 数学建模——线性规划篇
  9. 橡胶软接头的安装及使用说明
  10. LayaBox微信小游戏截图功能 利用微信API实现完美截图
  11. Bomblab(ICS课程回课pku)
  12. huffman python,哈夫曼(Huffman)编码python代码实现
  13. 乐玩插件和大漠插件哪个好_哪个PS后期插件功能最多最强?风光人像全能修图王!一个顶五个...
  14. nodejs模块加载的猜想
  15. PS海报制作的常用方法
  16. 使用 Web3.js 进行 Matic 测试网上链操作
  17. ucenter java版客户端
  18. 4 ARM PEG20K MAL
  19. 创建一个简单的workflow工作流(WF4)
  20. php获取当前时间戳和unix,Unix 时间戳与日期

热门文章

  1. 快速格式化的DOS命令是什么?
  2. 卫片执法-外业核查已上传照片下载
  3. JSP还有必要学吗?这篇文章告诉你
  4. java之获得当天的年份,月份,零时零分零秒
  5. NXP S32V APEX编程概述
  6. 关于nginx启用HTTP2后出现ERR_HTTP2_INADEQUATE_TRANSPORT_SECURITY错误的解决方案
  7. phontomjs webpage模块属性
  8. 九个月的腾讯外包体验总结
  9. 条件随机场实现命名实体识别
  10. 半小时学会Android软件版本更新