它是字节跳动开源的云原生数据仓库,采用存储计算分离架构,支持主流OLAP引擎优化技术,实现租户资源隔离、弹性扩缩容、数据读写强一致性。“基于共享存储的选举”是其重要功能。本文将详细介绍其基于存储计算分离架构的设计思维与实践。
项目地址:
在传统常见的分布式共享微服务架构中,我们通常使用DNS等成熟的解决方案来实现节点间的服务发现,使用Etcd等成熟的组件来实现副本节点间的选举,从而实现集群的高可用,在配置、使用、运维管理上都具有一定的复杂性。
随着越来越多的分布式系统采用高可用存储实现共享的存储计算分离架构,我们可以利用这种高可用存储来模拟单机系统中的共享内存,将不同的计算节点看作单机系统中的进程(线程),模仿单机系统的解决方案,实现它们之间的发现与同步。
本文介绍上述思想在开源云原生数据仓库中是如何设计和实现的。
《浅谈存储计算分离架构及优势》介绍了基于开源云原生数据仓库的存储计算分离架构。
可以看到,在计算端,有多个控制节点,需要通过多副本+主选举的方式提供高可用的服务能力,比如上图中的 / 。实际中,多个计算也需要选择一个单一节点来执行具体的读写任务。
-(以下简称“)组件最早是用来选举的,该组件基于Raft,提供了兼容的选举接口,实际使用中遇到了以下运维问题:
1. 至少需要部署3个节点,以提供单节点故障的容灾能力。这是因为Raft协议需要半数以上的节点正常运行,才能维持主节点的正常运行和选举。
2. 节点增删、服务发现流程复杂,需要修改所有节点的配置文件才能生效,所有调用方也需要修改配置才能发现结果。目前已经实现了使用固定共享域名的方案,不再为每个节点配置地址,但这又带来了处理域名解析可访问节点数与配置节点数不一致的复杂性。
3.容器重启后如果服务IP和服务端口发生变化,很难快速恢复,这不仅是因为2,还因为raft实现是和监听地址强绑定的。
我们可以将上述问题分为:
1.发生故障时的灾难恢复性能。
2.高可用性运维和部署成本。
考虑到作为一个新的云原生服务,不需要兼容接入,所以我们选择了基于存储计算分离的云原生架构,实现了新的选举方法来优化上述问题。
/ 术语定义 /
副本:具有相同状态的服务的多个已部署实例。
业务:除选举之外的服务逻辑。
:副本中无法提供业务服务的节点。
:副本中能够提供业务服务的节点。本文中经常将选举称为“ 选举”。
客户端:需要访问提供业务服务的节点。
/设计思维/
我们注意到,如果计算机试图同步多个线程对关键资源的访问,则常见的内存锁实现非常简单,依赖于以下基础:
1. 该锁分配在所有线程可见的内存中;
2.内存通过CAS(And Swap)指令支持小对象的原子写入;
3.内存支持保证原子写入的结果,读者看到的写入顺序与写入者的写入顺序相同;
4、操作系统内核支持通过futex等系统调用指令原子地等待/通知线程某个值的变化,以便线程知道某个资源可以再次竞争。
如果我们将多个尝试选举的节点看作不同的线程,将支持事务提交、且可见性顺序等于事务提交顺序的DB(一个用于存储元数据的高可用KV存储,以下简称“FDB”)看作支持CAS写、保证可见性顺序的本地内存,利用节点周期性的Get轮询来模拟Linux内核的线程唤醒通知机制,那么我们就可以利用模拟CAS操作的高可用DB KV存储来同步多个节点对“谁是王者”这个问题答案的竞争:谁CAS成功,谁就是胜者。
解决了竞争写者的同步问题之后,我们还需要将写者竞争的结果发布给读者。Linux 的锁数据结构会记录谁是互斥锁的拥有者,这里我们也可以将拥有者的监听地址写入竞争结果中:CAS key 写入的值需要包含自己的监听地址。因此读者可以通过访问此 key 来完成服务发现(读者不需要知道非拥有者的地址)。
/设计目标/
我们期望实现以下目标:
1、选举组件以库的形式嵌入到业务服务中供使用,类似Linux互斥锁使用的库。
2.支持任意数量的副本节点。
3.添加或删除节点不需要额外的操作。
4. 改变节点的监听地址不需要额外的操作。
5、只要有副本节点可用,主节点选举就能成功。这是因为在存储计算分离场景下,节点本地是无状态的,任意节点都可以成为主节点,而不需要从其他副本同步状态到本地节点。
6、副本节点之间不需要互相通信和服务发现,包括不需要物理时钟同步。
接下来我们通过几种分布式共识来阐述如何实现这些目标:
1.对“谁是新人”达成共识。
2、新老主席就“如何保证离任与就任时间不重叠”达成共识。
服务器节点在改变配置的时候,会对每轮选举中的“选举时间参数”达成共识。
3、客户端如何感知服务端就“谁是新来的”产生的共识?
/选举实施/
1.数据结构
分布式系统具有很多单机系统所不具备的复杂性,其中一个最重要的复杂性来源是有限的操作时限和非全连接拓扑导致的不可访问性:单机系统中任何读写内存的操作都没有“超时”或失败的概念,而分布式系统必须考虑这一点才能保证可用性。
那么如上图所示,CAS编写的数据结构,除了自己的监听地址之外,还需要包含有时间限制的状态信息租约:比如上任的时间,最近一次刷新的时间(如果有变化,则证明还活着),刷新时间间隔要求,多久不刷新就认为任期结束(其他节点可以重新开始竞争),状态等。
2. 选举基本规则
每个节点要么是 ,要么是 。预计在系统中的任何时间点,只有一个节点认为自己是 。
任何一个节点都可以读取KV存储中的一个key(后面简称“key”)来查询“谁是”的结果,如果这个key不存在,则说明自己从来没有被选举成功过。
CAS 定期更新租约。字段存储密钥中的值(以下称为“值”),将其期限延长至租约。+租约。
当服务以可控的方式停止时,比如某个进程结束时,你可以使用CAS将租约字段的值更新为Yield,主动放弃自己的身份。
每次周期性的GET都会读取该值,以确认自己是否已经成功当选、任期是否已到期、或者是否已经空出。如果是,CAS会尝试更新要竞选的key的值,并将其更改为自己的地址。
接下来我们将详细阐述这个规则,并介绍如何实际完成完整的选举过程。
3.替代方案
先决条件:
当前节点是 。
前提条件是:
1. 每个节点启动后都认为自己。a. 每一个节点在租约到期前,如果未能成功续约,则认为租约已经过期(即now()
2. 当续租期间CAS失败时,每个人发现其他人已成为所有者,并自动假定自己是所有者。
3、当某个进程结束或者其他服务以可控的方式停止时,可以使用CAS将value的lease字段更新为Yield,无论结果是否成功,都自动认为是Yield。
行动:
每次租约时,轮询密钥以检查密钥是否存在或值中的期限是否已过期。
4. 活动
先决条件:
1.目前的节点是。对于存储计算分离服务,我们认为每一个无状态副本都可以参与选举,状态机同步进度没有差异。
2.key不存在,或者value中的term已经过期,或者value中的lease为Yield,或者value是自己的监听地址。
行动:
1.如果key不存在,则使用Put if not exist写入自己的地址信息。
2.如果value中的期限已经过期或者value是自己的监听地址,Put CAS写入自己的地址信息。
3.lease.=Ready,lease.=lease.=now(),lease.和lease.都是配置文件中的信息。
5. 赢得选举
先决条件:
1.当前节点是。
2、当前节点写入值成功;或者虽然CAS失败,但是查到的值是自己的监听地址。
行动:
检查期限是否已过,即当前时间 now() 是否满足 now()
6. 就业
先决条件:
当前节点赢得选举,且其任期尚未到期。
行动:
1、调用业务方注册的()回调,提醒业务可以通过这种方式提供服务。对于有状态的服务,这个过程中可能需要同步一些状态,才可以通过这种方式提供服务;对于无状态的服务(比如存储与计算分离的计算节点),赢了就可以立刻启动服务。
2.提供()接口进行业务调用检验,目前只在now()中
3、提供yield()接口供业务调用,可触发主动离职流程。
7. 续约
先决条件
1. 当前节点为 ,且任期未届满,可无限连任。
2. 从选举或者上次重新选举到现在的时间大于或者等于lease.,也就是now()>=lease.+lease.
行动
CAS 设置值的租约。= now()。
8. 自愿辞职
先决条件
1.当前节点是。
2、业务方调用yield()接口,常见于服务退出等场景。
9. 消极顺从
先决条件
1.当前节点是。
2.now()>=lease. + lease. 或者在CAS中更新lease的时候发现已经被别的节点提前更新了。
行动
1.调用业务方注册的()回调,提醒业务不能再通过该方式提供服务。
2.当前节点变成。
10. 总结
如果我们回顾预期目标,就会发现它们都已经实现了。
1、支持任意数量的副本节点,它们只需要与共享存储FDB进行通信。
2. 添加或删除节点不需要额外的操作。这是因为节点之间不会相互发现或通信。
3. 节点改变监听地址不需要额外操作,因为节点通过CAS主动写入自己的监听地址,不需要像Raft那样显式地减少和增加节点。
4、只要有副本节点可用,主节点选举就成功。这是因为对于存储和计算分离的无状态节点,任何副本都可以成为主节点。
5. 副本节点之间不需要互相通信来进行同步和服务发现,包括物理时钟同步。
但是,如果不进行物理时钟同步,这两个术语是否会重叠,从而给集群服务带来风险呢? 下一节我们将分析这个问题。
/ 新老领导人的时代共识 /
1.问题描述
我们可以看到老节点在选举成功之后,可以立刻开始提供服务,那是不是整个集群就2个节点,都提供服务呢?
首先明确需求:
1.新员工入职后,原员工将不再以其身份响应新的请求。
2. 新员工上岗后,原员工已经处理过的所有请求都可以继续处理。
满足上述要求需要以下保障:
1. 任意两个节点的任期不重叠。也就是说,不会出现节点 a 的任期尚未结束,而节点 b 的任期已经开始的情况。
2、业务服务在响应请求的时候,总是先调用主选组件提供的()接口来检查期限是否已经过期。
第二点可以通过改造业务服务来满足,第一点需要根据术语的设计和实现来实现安全性。
2.问题分析
如果要保证在全局时钟下两个任期的任期不重叠,我们只需要保证:
假设1:任何人相信某人的任期结束于时间点A的概率都大于他相信自己任期结束于时间点B的概率。
因为选举成功后A必须小于任期开始时间。这样,任何两个任期之间就不会有重叠。任期结束时间通常由任期开始时间决定。为了方便工程实践,我们可以将假设1转化成:
假设1a:感知的任期开始时间大于感知的自身任期开始时间。
如果我们认为和在任期内的时钟计时误差小于两人认为任期开始的时间点之差,那么显然当假设 1a 成立时,假设 1 也成立。接下来我们尝试找到一个可以实现假设 1a 的实际解决方案。
3. 问题解决方案
如果我们怀疑两任任期重叠,那么旧任必须有一次成功的CAS写续自己的任期,当第一个任期重叠的新任成功就任时,必须有旧的续约契约读成功、本任任期已到期的判决、以及选举期间自己任期的CAS写成功。
如上图所示:旧任期对上一个任期的最后一次续约写入开始于 T_w0,在 T_w1 收到回复;新任期对最后一个租约的第一次读取开始于 T_r0,在 T_r1 收到回复;选举写入开始于 T_w2,在 T_w3 收到回复。假设这些值是一个虚拟但精确的全局时钟给出的时间戳。
从单机角度来说,肯定有一个数量级:
T_w0 < T_w1T_r0 < T_r1 < T_w2 < T_w3
由于下一个新term成功读取了上一个写入旧term的term的lease,这说明写入和读取之间肯定存在“-”的关系,因此从多台机器的角度来看也存在一个大小关系顺序:
T_w0 < T_r1
我们将利用这种关系给出与假设 1a 一致的间隔一词的定义。假设时间一词固定为:
1. 他的任期的旧观点是[T_w0, T_w0+)
2. (未来)认为旧术语为 [T_r1, T_r1+)
3. Xin 认为他的术语是 [T_w2, T_w2+)
也就是说,一方始终认为自己的任期开始时间是其最近一次成功写入 FDB 的开始时间(竞选或连任的开始时间),而其他一方则认为本任期开始时间是本任期第一次读取的结束时间(成功得知选举或连任的时间)。
旧任期结束时间是 T_r1+
扫一扫在手机端查看
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。