您的位置:首页 > 数据库 > Redis

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