php的memcached分布式hash算法,如何解决分布不均?crc32这个算法没办法把key值均匀的分布出去memcached的总结和分布式一致性hash
当前很多大型的web系统为了减轻数据库服务器负载,会采用memchached作为缓存系统以提高响应速度 。
目录: ()
memchached简介
hash
取模
一致性hash
虚拟节点
源码解析
【PHP分布式系统数据统一的简单介绍】参考资料
1. memchached简介
memcached是一个开源的高性能分布式内存对象缓存系统 。
其实思想还是比较简单的 , 实现包括server端(memcached开源项目一般只单指server端)和client端两部分:
server端本质是一个in-memory key-value store , 通过在内存中维护一个大的hashmap用来存储小块的任意数据,对外通过统一的简单接口(memcached protocol)来提供操作 。
client端是一个library , 负责处理memcached protocol的网络通信细节,与memcached server通信,针对各种语言的不同实现分装了易用的API实现了与不同语言平台的集成 。
web系统则通过client库来使用memcached进行对象缓存 。
2. hash
memcached的分布式主要体现在client端 , 对于server端,仅仅是部署多个memcached server组成集群,每个server独自维护自己的数据(互相之间没有任何通信),通过daemon监听端口等待client端的请求 。
而在client端,通过一致的hash算法,将要存储的数据分布到某个特定的server上进行存储 , 后续读取查询使用同样的hash算法即可定位 。
client端可以采用各种hash算法来定位server:
取模
最简单的hash算法
targetServer = serverList[hash(key) % serverList.size]
直接用key的hash值(计算key的hash值的方法可以自由选择,比如算法CRC32、MD5,甚至本地hash系统 , 如java的hashcode)模上server总数来定位目标server 。这种算法不仅简单,而且具有不错的随机分布特性 。
但是问题也很明显 , server总数不能轻易变化 。因为如果增加/减少memcached server的数量,对原先存储的所有key的后续查询都将定位到别的server上,导致所有的cache都不能被命中而失效 。
一致性hash
为了解决这个问题,需要采用一致性hash算法(consistent hash)
相对于取模的算法,一致性hash算法除了计算key的hash值外,还会计算每个server对应的hash值,然后将这些hash值映射到一个有限的值域上(比如0~2^32) 。通过寻找hash值大于hash(key)的最小server作为存储该key数据的目标server 。如果找不到,则直接把具有最小hash值的server作为目标server 。
为了方便理解,可以把这个有限值域理解成一个环,值顺时针递增 。
如上图所示,集群中一共有5个memcached server,已通过server的hash值分布到环中 。
如果现在有一个写入cache的请求,首先计算x=hash(key),映射到环中,然后从x顺时针查找,把找到的第一个server作为目标server来存储cache , 如果超过了2^32仍然找不到,则命中第一个server 。比如x的值介于A~B之间,那么命中的server节点应该是B节点
可以看到,通过这种算法,对于同一个key,存储和后续的查询都会定位到同一个memcached server上 。
那么它是怎么解决增/删server导致的cache不能命中的问题呢?
假设 , 现在增加一个server F , 如下图
此时,cache不能命中的问题仍然存在,但是只存在于B~F之间的位置(由C变成了F),其他位置(包括F~C)的cache的命中不受影响(删除server的情况类似) 。尽管仍然有cache不能命中的存在,但是相对于取模的方式已经大幅减少了不能命中的cache数量 。
虚拟节点
但是,这种算法相对于取模方式也有一个缺陷:当server数量很少时,很可能他们在环中的分布不是特别均匀,进而导致cache不能均匀分布到所有的server上 。
如图,一共有3台server – 1,2,4 。命中4的几率远远高于1和2 。
为解决这个问题,需要使用虚拟节点的思想:为每个物理节点(server)在环上分配100~200个点,这样环上的节点较多,就能抑制分布不均匀 。
当为cache定位目标server时,如果定位到虚拟节点上,就表示cache真正的存储位置是在该虚拟节点代表的实际物理server上 。
另外,如果每个实际server的负载能力不同,可以赋予不同的权重 , 根据权重分配不同数量的虚拟节点 。
// 采用有序map来模拟环
this.consistentBuckets = new TreeMap();
MessageDigest md5 = MD5.get();//用MD5来计算key和server的hash值
// 计算总权重
if ( this.totalWeightfor ( int i = 0; ithis.weights.length; i)
this.totalWeight= ( this.weights[i] == null ) ? 1 : this.weights[i];
} else if ( this.weights == null ) {
this.totalWeight = this.servers.length;
}
// 为每个server分配虚拟节点
for ( int i = 0; iservers.length; i) {
// 计算当前server的权重
int thisWeight = 1;
if ( this.weights != nullthis.weights[i] != null )
thisWeight = this.weights[i];
// factor用来控制每个server分配的虚拟节点数量
// 权重都相同时,factor=40
// 权重不同时 , factor=40*server总数*该server权重所占的百分比
// 总的来说,权重越大,factor越大,可以分配越多的虚拟节点
double factor = Math.floor( ((double)(40 * this.servers.length * thisWeight)) / (double)this.totalWeight );
for ( long j = 0; jfactor; j) {
// 每个server有factor个hash值
// 使用server的域名或IP加上编号来计算hash值
// 比如server - "172.45.155.25:11111"就有factor个数据用来生成hash值:
// 172.45.155.25:11111-1, 172.45.155.25:11111-2, ..., 172.45.155.25:11111-factor
byte[] d = md5.digest( ( servers[i]"-"j ).getBytes() );
// 每个hash值生成4个虚拟节点
for ( int h = 0 ; h4; h) {
Long k =
((long)(d[3 h*4]0xFF)24)
| ((long)(d[2 h*4]0xFF)16)
| ((long)(d[1 h*4]0xFF)8 )
| ((long)(d[0 h*4]0xFF));
// 在环上保存节点
consistentBuckets.put( k, servers[i] );
}
}
// 每个server一共分配4*factor个虚拟节点
}
// 采用有序map来模拟环
this.consistentBuckets = new TreeMap();
MessageDigest md5 = MD5.get();//用MD5来计算key和server的hash值
// 计算总权重
if ( this.totalWeightfor ( int i = 0; ithis.weights.length; i)
this.totalWeight= ( this.weights[i] == null ) ? 1 : this.weights[i];
} else if ( this.weights == null ) {
this.totalWeight = this.servers.length;
}
// 为每个server分配虚拟节点
for ( int i = 0; iservers.length; i) {
// 计算当前server的权重
int thisWeight = 1;
if ( this.weights != nullthis.weights[i] != null )
thisWeight = this.weights[i];
// factor用来控制每个server分配的虚拟节点数量
// 权重都相同时,factor=40
// 权重不同时,factor=40*server总数*该server权重所占的百分比
// 总的来说 , 权重越大,factor越大,可以分配越多的虚拟节点
double factor = Math.floor( ((double)(40 * this.servers.length * thisWeight)) / (double)this.totalWeight );
for ( long j = 0; jfactor; j) {
// 每个server有factor个hash值
// 使用server的域名或IP加上编号来计算hash值
// 比如server - "172.45.155.25:11111"就有factor个数据用来生成hash值:
// 172.45.155.25:11111-1, 172.45.155.25:11111-2, ..., 172.45.155.25:11111-factor
byte[] d = md5.digest( ( servers[i]"-"j ).getBytes() );
// 每个hash值生成4个虚拟节点
for ( int h = 0 ; h4; h) {
Long k =
((long)(d[3 h*4]0xFF)24)
| ((long)(d[2 h*4]0xFF)16)
| ((long)(d[1 h*4]0xFF)8 )
| ((long)(d[0 h*4]0xFF));
// 在环上保存节点
consistentBuckets.put( k, servers[i] );
}
}
// 每个server一共分配4*factor个虚拟节点
}
// 用MD5来计算key的hash值
MessageDigest md5 = MD5.get();
md5.reset();
md5.update( key.getBytes() );
byte[] bKey = md5.digest();
// 取MD5值的低32位作为key的hash值
long hv = ((long)(bKey[3]0xFF)24) | ((long)(bKey[2]0xFF)16) | ((long)(bKey[1]0xFF)8 ) | (long)(bKey[0]0xFF);
// hv的tailMap的第一个虚拟节点对应的即是目标server
SortedMap tmap = this.consistentBuckets.tailMap( hv );
return ( tmap.isEmpty() ) ? this.consistentBuckets.firstKey() : tmap.firstKey();
更多问题到问题求助专区()
php mysql分布式数据库如何实现当前做分布式的厂商有几家,我知道比较出名的有“华为云分布式数据库DDM”和“阿里云分布式数据库”,感兴趣可以自行搜素了解下 。
分布式数据库的几点概念可以了解一下 。
数据分库:
以表为单位,把原有数据库切分成多个数据库 。切分后不同的表存储在不同的数据库上 。
以表中的数据行记录为单位,把原有逻辑数据库切分成多个物理数据库分片,表数据记录分布存储在各个分片上 。
路由分发:
在分布式数据库中,路由的作用即将SQL语句进行解析 , 并转发到正确的分片上,保证SQL执行后得到正确的结果,并且节约QPS资源 。
读写分离:
数据库中对计算和缓存资源消耗较多的往往是密集或复杂的SQL查询 。当系统资源被查询语句消耗,反过来会影响数据写入操作 , 进而导致数据库整体性能下降,响应缓慢 。因此,当数据库CPU和内存资源占用居高不下,且读写比例较高时,可以为数据库添加只读数据库 。
分布式系统为什么要同步,同步所需要的构件有哪些在Zookeeper的官 网上有这么一句话:ZooKeeper is a centralized service for maintaining configuration information, naming, providing distributed synchronization, and providing group services. 这大概描述了Zookeeper主要可以干哪些事情:配置管理,名字服务,提供分布式同步以及集群管理 。那这些服务又到底是什么呢?我们为什么需要这样的服务?我们又为什么要使用Zookeeper来实现呢 , 使用Zookeeper有什么优势?接下来我会挨个介绍这些到底是什么,以及有哪些开源系统中使用了 。配置管理在我们的应用中除了代码外 , 还有一些就是各种配置 。比如数据库连接等 。一般我们都是使用配置文件的方式,在代码中引入这些配置文件 。但是当我们只有一种配置,只有一台服务器,并且不经常修改的时候,使用配置文件是一个很好的做法,但是如果我们配置非常多,有很多服务器都需要这个配置,而且还可能是动态的话使用配置文件就不是个好主意了 。这个时候往往需要寻找一种集中管理配置的方法,我们在这个集中的地方修改了配置 , 所有对这个配置感兴趣的都可以获得变更 。比如我们可以把配置放在数据库里,然后所有需要配置的服务都去这个数据库读取配置 。但是,因为很多服务的正常运行都非常依赖这个配置,所以需要这个集中提供配置服务的服务具备很高的可靠性 。一般我们可以用一个集群来提供这个配置服务,但是用集群提升可靠性,那如何保证配置在集群中的一致性呢? 这个时候就需要使用一种实现了一致性协议的服务了 。Zookeeper就是这种服务,它使用Zab这种一致性协议来提供一致性 。现在有很多开源项目使用Zookeeper来维护配置 , 比如在HBase中,客户端就是连接一个Zookeeper , 获得必要的HBase集群的配置信息,然后才可以进一步操作 。还有在开源的消息队列Kafka中,也使用Zookeeper来维护broker的信息 。在Alibaba开源的SOA框架Dubbo中也广泛的使用Zookeeper管理一些配置来实现服务治理 。名字服务名字服务这个就很好理解了 。比如为了通过网络访问一个系统,我们得知道对方的IP地址,但是IP地址对人非常不友好,这个时候我们就需要使用域名来访问 。但是计算机是不能是别域名的 。怎么办呢?如果我们每台机器里都备有一份域名到IP地址的映射 , 这个倒是能解决一部分问题,但是如果域名对应的IP发生变化了又该怎么办呢?于是我们有了DNS这个东西 。我们只需要访问一个大家熟知的(known)的点,它就会告诉你这个域名对应的IP是什么 。在我们的应用中也会存在很多这类问题,特别是在我们的服务特别多的时候 , 如果我们在本地保存服务的地址的时候将非常不方便,但是如果我们只需要访问一个大家都熟知的访问点,这里提供统一的入口,那么维护起来将方便得多了 。分布式锁其实在第一篇文章中已经介绍了Zookeeper是一个分布式协调服务 。这样我们就可以利用Zookeeper来协调多个分布式进程之间的活动 。比如在一个分布式环境中,为了提高可靠性,我们的集群的每台服务器上都部署着同样的服务 。但是 , 一件事情如果集群中的每个服务器都进行的话,那相互之间就要协调,编程起来将非常复杂 。而如果我们只让一个服务进行操作 , 那又存在单点 。通常还有一种做法就是使用分布式锁,在某个时刻只让一个服务去干活,当这台服务出问题的时候锁释放,立即fail over到另外的服务 。这在很多分布式系统中都是这么做 , 这种设计有一个更好听的名字叫Leader Election(leader选举) 。比如HBase的Master就是采用这种机制 。但要注意的是分布式锁跟同一个进程的锁还是有区别的,所以使用的时候要比同一个进程里的锁更谨慎的使用 。集群管理在分布式的集群中 , 经常会由于各种原因,比如硬件故障,软件故障,网络问题,有些节点会进进出出 。有新的节点加入进来,也有老的节点退出集群 。这个时候,集群中其他机器需要感知到这种变化,然后根据这种变化做出对应的决策 。比如我们是一个分布式存储系统,有一个中央控制节点负责存储的分配 , 当有新的存储进来的时候我们要根据现在集群目前的状态来分配存储节点 。这个时候我们就需要动态感知到集群目前的状态 。还有,比如一个分布式的SOA架构中,服务是一个集群提供的,当消费者访问某个服务时,就需要采用某种机制发现现在有哪些节点可以提供该服务(这也称之为服务发现,比如Alibaba开源的SOA框架Dubbo就采用了Zookeeper作为服务发现的底层机制) 。还有开源的Kafka队列就采用了Zookeeper作为Cosnumer的上下线管理 。后记在这篇文章中 , 列出了一些Zookeeper可以提供的服务,并给出了一些开源系统里面的实例 。后面我们从Zookeeper的安装配置开始,并用示例进一步介绍Zookeeper如何使用 。(转载)
php能实现分布式数据库吗?可以实现.
将数据库放在不同的服务器上,主页的不同模块可以单独访问自己所需要的数据库,以减轻单独一个服务器的压力.
既可以每个模块都是不同数据库,也可以同个模块不同数据库,但这样没什么意思.
实际上,现在网络带宽大,服务器性能也好,再加以磁盘阵列保证数据.如果吞吐量大得惊人,没必要用分布式的,必竟维护比较麻烦.
象很多网络游戏在线人数那么多,或者象天涯猫扑那样,才需要用分布式,普通网站就几乎都用集中式的.
分布式系统常用的一致性算法有哪些在做服务器负载均衡时候可供选择的负载均衡的算法有很多,包括:轮循算法(Round Robin)、哈希算法(HASH)、最少连接算法(Least Connection)、响应速度算法(Response Time)、加权法(Weighted )等 。其中哈希算法是最为常用的算法.典型的应用场景是: 有N台服务器提供缓存服务,需要对服务器进行负载均衡,将请求平均分发到每台服务器上 , 每台机器负责1/N的服务 。常用的算法是对hash结果取余数 (hash() mod N):对机器编号从0到N-1,按照自定义的hash()算法 , 对每个请求的hash()值按N取模,得到余数i,然后将请求分发到编号为i的机器 。但这样的算法方法存在致命问题,如果某一台机器宕机,那么应该落在该机器的请求就无法得到正确的处理,这时需要将当掉的服务器从算法从去除,此时候会有(N-1)/N的服务器的缓存数据需要重新进行计算;如果新增一台机器,会有N /(N 1)的服务器的缓存数据需要进行重新计算 。对于系统而言,这通常是不可接受的颠簸(因为这意味着大量缓存的失效或者数据需要转移) 。那么,如何设计一个负载均衡策略,使得受到影响的请求尽可能的少呢?在Memcached、Key-Value Store、Bittorrent DHT、LVS中都采用了Consistent Hashing算法,可以说Consistent Hashing 是分布式系统负载均衡的首选算法 。1、Consistent Hashing算法描述下面以Memcached中的Consisten Hashing算法为例说明 。由于hash算法结果一般为unsigned int型,因此对于hash函数的结果应该均匀分布在[0,232-1]间,如果我们把一个圆环用232 个点来进行均匀切割,首先按照hash(key)函数算出服务器(节点)的哈希值, 并将其分布到0~232的圆上 。用同样的hash(key)函数求出需要存储数据的键的哈希值,并映射到圆上 。然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器(节点)上 。Consistent Hashing原理示意图新增一个节点的时候 , 只有在圆环上新增节点逆时针方向的第一个节点的数据会受到影响 。删除一个节点的时候 , 只有在圆环上原来删除节点顺时针方向的第一个节点的数据会受到影响,因此通过Consistent Hashing很好地解决了负载均衡中由于新增节点、删除节点引起的hash值颠簸问题 。Consistent Hashing添加服务器示意图虚拟节点(virtual nodes):之所以要引进虚拟节点是因为在服务器(节点)数较少的情况下(例如只有3台服务器),通过hash(key)算出节点的哈希值在圆环上并不是均匀分布的(稀疏的),仍然会出现各节点负载不均衡的问题 。虚拟节点可以认为是实际节点的复制品(replicas),本质上与实际节点实际上是一样的(key并不相同) 。引入虚拟节点后 , 通过将每个实际的服务器(节点)数按照一定的比例(例如200倍)扩大后并计算其hash(key)值以均匀分布到圆环上 。在进行负载均衡时候,落到虚拟节点的哈希值实际就落到了实际的节点上 。由于所有的实际节点是按照相同的比例复制成虚拟节点的 , 因此解决了节点数较少的情况下哈希值在圆环上均匀分布的问题 。虚拟节点对Consistent Hashing结果的影响从上图可以看出,在节点数为10个的情况下,每个实际节点的虚拟节点数为实际节点的100-200倍的时候,结果还是很均衡的 。第3段中有这些文字:“但这样的算法方法存在致命问题,如果某一台机器宕机,那么应该落在该机器的请求就无法得到正确的处理,这时需要将当掉的服务器从算法从去除 , 此时候会有(N-1)/N的服务器的缓存数据需要重新进行计算;”为何是 (N-1)/N 呢?解释如下:比如有 3 台机器,hash值 1-6 在这3台上的分布就是:host 1: 1 4host 2: 2 5host 3: 3 6如果挂掉一台,只剩两台,模数取 2,那么分布情况就变成:host 1: 1 3 5host 2: 2 4 6可以看到,还在数据位置不变的只有2个: 1 , 2,位置发生改变的有4个,占共6个数据的比率是 4/6 = 2/3这样的话,受影响的数据太多了,势必太多的数据需要重新从 DB 加载到 cache 中,严重影响性能【consistent hashing 的办法】上面提到的 hash 取模,模数取的比较小 , 一般是负载的数量 , 而 consistent hashing 的本质是将模数取的比较大,为 2的32次方减1,即一个最大的 32 位整数 。然后,就可以从容的安排数据导向了,那个图还是挺直观的 。以下部分为一致性哈希算法的一种PHP实现 。点击下载
关于PHP分布式系统数据统一和的介绍到此就结束了,不知道你从中找到你需要的信息了吗 ?如果你还想了解更多这方面的信息,记得收藏关注本站 。
推荐阅读
- 冰雪传奇手机直播教程,手机版冰雪传奇能赚钱吗?如何操作
- 国外视频下载网站,国外视频下载网站有哪些
- 学拍摄选什么相机最好,摄影爱好者初学买个什么相机
- 网络代理哪个平台反水高,网络平台代理赚流水
- python类内调用函数 python类中调用内部函数
- 腾讯超级播放器flutter,腾讯超级播放器sdk
- linux脚本命令行交互,linux使用脚本执行命令
- css居中浮动代码,css居中代码怎么写
- 用java代码实现链表 java怎么写链表