热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

分布式缓存算法和Infinispan

分布式缓存概念已经出现很久了,目前也有很多经典的应用,比如Memcached,Redis和笔者工作中用到的Infinispan。其背后的实现原理是怎么样的?例如:对象是如何分配到多

分布式缓存概念已经出现很久了,目前也有很多经典的应用,比如Memcached, Redis和笔者工作中用到的Infinispan。其背后的实现原理是怎么样的?例如:对象是如何分配到多个节点的?如果增加或删除一个节点,已有的缓存数据要重新排列吗?笔者带着这些问题查找资料后形成此文。

此文包括以下3个部分:
1. 为什么要用分布式缓存
2. 分布式缓存依赖的算法
3. Infinispan对分布式缓存实现

一,为什么要用分布式缓存
要回答为什么用分布式缓存之前,要回答为什么要缓存?缓存的目的是通过数据放入内存加快数据访问速度,并且降低对瓶颈资源的访问压力。为什么要用分布式缓存呢?当缓存不够用时,需要增大缓存,其中一个方向是增加内存条,但是受制于主板和CPU等硬件的限制,增加内存条必然是有限的。另一个方向就是分布式缓存,即把缓存分布到多台机器上,缓存容量和节点数量呈线性关系增长。

二,分布式缓存依赖的算法
既然数据存到多台机器上,那么怎么合理均匀地讲数据分布到多台机器呢?一个直观的思路是类似Hashtable的算法,每个节点作为一个hash槽,按照对象的Hash code对节点取余数,然后存入或获取相应的节点。简短描述如下:
            Hash(o) mod n
            – o 是数据对象
            – n 是节点数量
举个例子,假设Hash code为1-12这12个数字,存放到4个节点。如果按照上述算法,余数分别是0,1,2,3. 存储结构如下图所示:

《分布式缓存算法和Infinispan》 4节点Mod Hash

存取和数据分布都很完美,但是当一台机器坏了的话,问题就来了。4个节点变成3个节点。按理应该对3取余数,存储结构变成下图所示:

《分布式缓存算法和Infinispan》 3节点Mod Hash

旧节点上的数据几乎全部都失效,意味着需要重新转移数据,当存放数据的数据量很大,转移数据会用很好时间,在此期间,应用无法使用缓存,对应用的影响就很大。如果有一种算法能够达到下图所示,那么就完美了,即正常节点上的数据不动,后续把坏节点的数据均匀地缓存到正常节点上。还有个前提,存在一种算法,保证缓存的对象能正确地获取:

《分布式缓存算法和Infinispan》 合理的Hash

可惜,事实证明,按照旧的取余数思路无法达到上述效果。
于是为解决分布式问题的另一种算法出现了:Consistent hashing,中文名一致性哈希。

看看Consistent hashing描述:
        当hashtable变化时,只有K / n 个数据需要重排。 K是数据的总数,n是Hash 槽的数量。

对照上面的例子,恰好12/4=3个节点需要重排。

顺便说一下,它是MIT的Karger教授在1997年发明的。那么它在技术上是如何实现的呢?

1,将真实的机器节点名虚拟成多个虚拟节点。例如节点A,虚拟为A1,A2,A3,A4等。
2,对数据和虚拟节点都按照某Hash方法取得Hash code。
3,将Hash槽安排成一个0 到 2的32次方-1的环。
4,按照第2步中的数据和虚拟节点。按照Hash code值安排到第3步的Hash槽上。

举例:有A,B,C三个物理节点,分别虚拟为4个虚拟节点。缓存对象用点表示,节点用小圆圈表示。

《分布式缓存算法和Infinispan》 Consistent Hash技术

5,如何获取数据所在的机器节点呢?找到数据的hashcode,顺时针查找下一个虚拟节点,这样就找到了所在的物理节点。
6,如果某个物理节点损坏了,怎么办?顺时针找已损坏节点的下一个节点,把损坏节点上的数据存入它。

举例:当C损坏,B1-C1上的所有数据,后续将存入A2;A2-C2的所有数据,后续将存入B2;A3-C3的所有数据都存入B3;B4-C4的所有数据将存入A1。相当于C节点的所有数据被A和B分担了。

《分布式缓存算法和Infinispan》 ConsistentHash对拓扑变化的处理

7,如果向集群加一个物理节点,恰好和6是反向操作。也就是说新节点将从已有的若干个节点上,各分担一部分数据。

总结Consistent hashing:当节点变化后,它实现了只动K/n个数据的关键是什么呢?第一,虚拟化物理节点;第二,可靠的Hash算法,即能将虚拟节点和数据均匀分布在Hash环上。

三,Infinispan对分布式缓存实现

Infinispan是什么?下面是官方介绍:

《分布式缓存算法和Infinispan》 What’s Infinispan

Infinispan 用JGroups作为集群管理设施,可以发现集群节点,向各节点传输数据。
Infinishpan是如何实现上述consistent hash的算法呢?

《分布式缓存算法和Infinispan》

Consistent hash将1到2的32次方-1的空间划分为若干Segment(numSegments由设置缓存的人定义),每个Segment分配Owner,即分配物理节点。分配好之后,这个Segment上的数据都存储在这个Owner里。

当拓扑结构变化后,如何重新计算各个Segment的Owner呢?
分析下面代码(在SyncConsistentHashFactory类里):

《分布式缓存算法和Infinispan》 PopulateOwner

输入参数numberSegments就是Segment的数量。
共两层循环,外层循环:只要存在Segment没有Owner,就执行内循环。内循环是逐个遍历物理节点,用物理节点名结合VirtualNode计算出一个segment,然后将当前的物理节点设置为Segment的Owner。如何计算Segment呢?

《分布式缓存算法和Infinispan》 ComputeSegment

用到一个HashFunction,实际用的就是MurMurHash,他是Austin Appleby在2008年发明的。具备更好的hash分布和更好的性能。

再来看看addPrimaryOwner是如何实现的?

《分布式缓存算法和Infinispan》 AddPrimaryOwner

有两个If判断,1. 当前segment没有owner才往下走;2. 当前节点允许分配的segment小于它承担的Segment数量才把当前节点分配给Segment。

如何知道Node该承担多少Segment呢?再看看这里的computeExpectedSegmentsForNode函数

《分布式缓存算法和Infinispan》 computeExpectedSegmentsForNode

这里做了一些什么事呢?就是要计算当前节点能承担多少个Segment。为什么做这件事呢?因为现实中,物理机器之间是有差别的,有些物理机器内存多,有些内存少,所以,内存多的节点理所当然要分担多一点内存。这也就有了第一行取节点的CapacityFactor。像下面这一行,实际就是算权重。
nodeSegments = nodeCapacityFactor / remainingCapacity * remainingCopies;

上面有addPrimaryOwner函数,那么是不是有其他Owner呢?确实是的。考虑到数据的节点的可靠性,给每个Segment分配了多个Owner,除了PrimaryOwner,可以有多个BackupOwner。doCopyOwners实现了如何分配backupOwner:逐个Segment扫描他们的Owner,如果和当前的Owner不一样,就可以分配为backupOwner。

《分布式缓存算法和Infinispan》 doCopyOwners

将Infinispan的源代码用图形方式再描述一遍。当某个节点损坏后,数据将如何转移?

《分布式缓存算法和Infinispan》 remove 1 node

原先Address2节点是第一块和第三块Segment的owner。当Address2移除后,意味着第一块和第三块Segment上缺少Owner,所以要重新分配owner。AddPrimaryOwner函数中,假设当虚拟节点循环到100的时候,当前正好是Address3,所以,将Address3作为第三块的PrimaryOwner。与此类似,当virtual节点循环到1000的时候,将address1分配为第一块Segment的Owner。可以想象,还有其他原先属于Address2的Segment会被分配到其他节点上。

当一个新节点加入到集群后,如何分配segment的Owner呢?

《分布式缓存算法和Infinispan》 Add 1 node

当新节点Address4加入后,重新计算Owner当Address1,Address2,Address3都被作为Owner后,轮流到给Address4找Segment,假设第三块的Segment恰好被找到,那么就可以更新它的PrimaryOwner值为Address4,原先的Address2-1就被冲掉了。可以想象,原先属于其他节点的其他Segment也可能会选Address4作为PrimaryOwner。

对比Consistent Hash的技术描述,Infinispan的segment实际就是hash环上虚拟节点之间的地址段。Segment数量就是所有虚拟节点的数量。不同点是Infinispan的寻址方式不是靠顺时针扫描,而是通过Owner映射表。
另外Infinispan考虑到数据可靠性,处理了backup数据的问题。考虑到各个机器的容量,分配更加合理的Segment数量。

全文总结:通过对ConsistentHash和Infinispan对此的实现分析,基本弄懂了分布式缓存背后的原理。通过Consistent Hash达到了两个目标:1. 数据在节点上均匀分布;2. 当拓扑结构变化后,K/n个数据被重新分布。


推荐阅读
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
  • 本文介绍了使用哈夫曼树实现文件压缩和解压的方法。首先对数据结构课程设计中的代码进行了分析,包括使用时间调用、常量定义和统计文件中各个字符时相关的结构体。然后讨论了哈夫曼树的实现原理和算法。最后介绍了文件压缩和解压的具体步骤,包括字符统计、构建哈夫曼树、生成编码表、编码和解码过程。通过实例演示了文件压缩和解压的效果。本文的内容对于理解哈夫曼树的实现原理和应用具有一定的参考价值。 ... [详细]
  • GreenDAO快速入门
    前言之前在自己做项目的时候,用到了GreenDAO数据库,其实对于数据库辅助工具库从OrmLite,到litePal再到GreenDAO,总是在不停的切换,但是没有真正去了解他们的 ... [详细]
  • Todayatworksomeonetriedtoconvincemethat:今天在工作中有人试图说服我:{$obj->getTableInfo()}isfine ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
  • Centos下安装memcached+memcached教程
    本文介绍了在Centos下安装memcached和使用memcached的教程,详细解释了memcached的工作原理,包括缓存数据和对象、减少数据库读取次数、提高网站速度等。同时,还对memcached的快速和高效率进行了解释,与传统的文件型数据库相比,memcached作为一个内存型数据库,具有更高的读取速度。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • LVS实现负载均衡的原理LVS负载均衡负载均衡集群是LoadBalance集群。是一种将网络上的访问流量分布于各个节点,以降低服务器压力,更好的向客户端 ... [详细]
  • HashMap的扩容知识详解
    本文详细介绍了HashMap的扩容知识,包括扩容的概述、扩容条件以及1.7版本中的扩容方法。通过学习本文,读者可以全面了解HashMap的扩容机制,提升对HashMap的理解和应用能力。 ... [详细]
author-avatar
mobiledu2502930533
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有