The binary search of distributed programming翻译
2016-04-28 09:54
471 查看
The binary search of distributed programming
简单的说就是如何实现一种方法,使redis集群提供一个逐步增加(monotonically increasing)的ID值(一致性算法)这时候是个简单的问题,但实际上要复杂的多,考虑到在任何情况下,都必须保证如下特性:产生的新的ID要比所有以往的id大,在不同的时刻不能产生不同的ID,而且考虑网络分区(network partitions)和其他的故障。当多数节点不可达时,系统将不可用,但是永远不能产生错误的ID,在后面还会看到,在高负载的情况下,我提出的算法会有活跃度( liveness issue )的问题
实际上,我想到了一种方案,但是并不高效,不适合那些每秒钟生成大量ID的操作。有许多复杂的分布式算法,例如raft,paxos,其很多特性都建立在这个递增id的功能上。我所提出的算法的奇妙之处在于它易于理解和实现。我可以说,这就是分布式算法中的二分查找,简单但是精妙。
然而我将算法进行修改使其可以在客户端上实现。幸运的是它依然是对的,但是我不会用这算法来优化Redlock,尝试解决这种问题是很好的练习,而且对于第一次接触这类分布式系统的现实问题的人而言这是很有意思的。
它是如何工作的
算法要求:1. 服务器支持set_if_less_than()操作。
2. 在回复客户端前,服务器可将写入数据 fsync()到硬盘
几乎所有SQL服务器,Redis,和很多其他存储系统都支持以上操作。
假定我们有N个节点,令N=5,首先将所有节点初始key current设定为0,用Redis命令来表示就是
SEt current 0
对于这5个节点而言,当集群初始化时需要执行以上初始化工作。
为了产生新的ID值:
得到多数节点中的key值(在这里N=5,所以至少需要3个节点)
如果不能得到至少3个节点的key,goto 1
得到所有key中最大的key值,并加一,称之为$NEXITD
对所有连接上的节点执行以下写操作
IF current < $NEXTID THEN SET current $NEXTID return $NEXTID ELSE return NULL END
如果超过3个节点成功返回$NEXIID,算法成功,我们得到了新的ID
否则,我们没有成功写入多数节点,goto 1
第四步可以用lua脚本表示为
local val = tonumber(redis.call('get',KEYS[1])) local nextid = tonumber(ARGV[1]) if val < nextid then redis.call('set',KEYS[1],nextid) return nextid else return nil end
这个算法是正确的吗
如果我们得到了多数节点的“投票”,对其它客户而言,它们不可能得到与之相同或者更大的ID,所以新产生的ID总是比以往的ID要打,同样的原因,不可能产生两个相同的ID为什么这个算法慢
这个算法的缺点在于它的并发访问。。如果多个客户尝试在同一时刻尝试得到新的ID,没有人能够得到多数选票,所以他们将重试,希望得到更大的ID号。同时这意味着在产生的ID序列中会有“空洞”,比如1,2,6,10,11,21.那些缺少的ID是由于并发访问导致的投票失败造成的(split brain )这里的split brain并不是指不同节点之间不一致的情况,而是说无法得到多数节点对某个新ID的投票支持。通常情况,split brain指集群中配置冲突,比如多个节点都宣布自己是master节点。我这里所说的split brain 情况与Raft中所指类似。
(附注:split brain 脑分裂:
在集群中,节点间通过某种机制(心跳)了解彼此的健康状态,以确保各节点协调工作。 假设只有”心跳”出现问题, 各个节点还在正常运行, 这时,每个节点都认为其他的节点宕机了, 自己是整个集群环境中的”唯一建在者”,自己应该获得整个集群的”控制权”。 在集群环境中,存储设备都是共享的, 这就意味着数据灾难, 这种情况就是”脑裂”
解决方法:这时就需要引入第三个设备:Quorum Device. Quorum Device 通常采用饿是共享磁盘,这个磁盘也叫作Quorum disk。 这个Quorum Disk 也代表一票。 当2个结点的心跳出现问题时,2个节点同时去争取Quorum Disk 这一票, 最早到达的请求被最先满足。 故最先获得Quorum Disk的节点就获得2票。另一个节点就会被剔除。)
在不发生以上所说的投票失败的前提下,每秒能够得到的ID取决于网络的往返延迟和并发的客户数量。要想解决这个问题,你可以引入一个ID server对请求进行协调,对客户的请求进行串行化(serializing)。使用这个策略可以获得5k id/sec的速率,
另外一种方法是不用协调节点,而是让客户以随机的间隔发出请求,失败后以指数增加的推迟时间进行重试。
英文原文
相关文章推荐
- redis安装问题小结
- 使用 Redis 和 Python 构建一个共享单车的应用程序
- Redis偶发连接失败案例实战记录
- Redis中实现查找某个值的范围
- redis的hGetAll函数的性能问题(记Redis那坑人的HGETALL)
- Redis和Memcached的区别详解
- 分割超大Redis数据库例子
- Redis总结笔记(一):安装和常用命令
- Redis sort 排序命令详解
- 用Redis实现微博关注关系
- Redis实现信息已读未读状态提示
- redis中修改配置文件中的端口号 密码方法
- 在Ruby on Rails上使用Redis Store的方法
- Redis和Memcache的区别总结
- 在Node.js应用中使用Redis的方法简介
- Redis服务器的启动过程分析
- web 应用中常用的各种 cache详解
- 利用yum安装Redis的方法详解
- 从MySQL到Redis的简单数据库迁移方法
- 为啥懒 Redis 是更好的 Redis