文章目录
引言
这篇文章主要是整理6.824一些课后学生提出的问题和老师做出的解答,学习这个一个方面是为了更好的多角度理解paper的内容,再一个就是日后重拾这些知识的时候能够回想的更快。文章的大部分内容是翻译,也在不少问题后也加上了我自己的看法。其次并没有把所有的问题尽数搬来,选择了一些我觉的有意义的问题,问题顺序相比于链接中是严格有序的。笔者水平有限,一些地方可能表达并不准确,可以留言或者在原文中查找。
正文
lec3 GFS
Q:为什么原子附加是至少一个不是恰好一次?
在论文3.1,第七步中,如果第一次写入失败,客户端会进行重试,这会导致数据在没有出现失败的副本中多次追加,所以可能会检测到重复的客户请求。但至少可以看到一条
Q:应用程序如何知道块的哪些部分由填充和重复记录组成?
为了检测出填充,引用可以设置一个可预测的长度在记录的开头(我认为是长度有效数据长度之类的东西),或者包含一个checksum。为了检测出重复,可以让每一条记录都包含一个唯一ID,使得读取幂等,GFS为了处理这些情况提供了一个库。
Q:因为原子追加到文件中的偏移量是不可预测的,client如何找到其加入的数据在文件中的偏移量?
使用append操作一般用户希望顺序的读取玩不的文件,用户将会扫描一遍文件以此得到有效的数据(填充和重复),所以不需要知道提前知道记录的位置,举个例子,在一个并发爬虫的得到的URL集合中,每一个URL的偏移没有扫描意义,我们需要的是一个集合。
Q:paper中提到了引用计数,这是什么?
这是实现快照的一部分,当GFS创建一个快照的时候,并不会拷贝这些chunk,而是会给这些chunk才呢过加一个引用计数,这使得创建一个快照很便宜,如果一个client写入一个chunk而且master注意到引用计数大于1(因为在拷贝时会取消作快照的文件的所有Chunk的租约master,所以新的写一定会想master请求元数据。3.4),master首先会创建一个副本为了客户端能够更新,这可以在一些块不被修改的情况下减少拷贝。
Q:如果应用程序使用标准POSIX文件API,是否需要对其进行修改才能使用GFS?
是的,因为GFS不是为了扩展现有的程序,它被设计用于新编写的程序,比如说MapReduce。
Q:GFS如何决定哪一台副本距离主服务器较近?(3.2)
paper中暗示GFS会根据存储可用副本的服务器的IP地址来执行此操作。在2003年时,Google必须以这样一种方式分配IP地址:如果两个IP地址在IP地址空间中彼此靠近,则它们在机房中也彼此靠近。
Q:假设S1是一个chunk的primary,master和S1之间出现了网络问题,master将会注意到并重新指定一个服务器为primary,我们称为S2。但是S1实际上并没有宕机,现在一个chunk有两个primary吗?
GFS的lease机制避免了这种情况,每一个chunk都会有一个租约,在租约结束以后会重新续约,且在租约之内master保证不会有其他服务器成为这个chunk的maste,这样S2一定会在S1停止服务因为才能成为此chunk的primary。
Q:64MB的大小对于chunk来说是不是太大了?
这么大的块可以减少master中的元数据数量。小于64MB的文件也不会得到太多的并行性。
Q:如何看待GFS以正确性换取性能和简单
这是分布式系统中一个永恒的主题,通过利用特殊的程序可以容忍更为宽松的一致性,我们可以设计出更好性能和足够的一致性的系统。
Q:master宕机了怎么办?
会有几台mater的副本,它们拷贝了master的所有状态。该论文的设计需要人工干预才能在主设备故障后切换到其中一个副本,但其实并不需要。(查资料得应该是Chubby在做这个事情)
Q:只有一个master是一个好的设计吗?
这个想法简化了最初的部署,但从长远来看并不是很好。这篇文章的作者说说,随着时间的流逝和GFS使用的增加,出现了一些错误。很多文件增长的很快使得master无法在一台机器的RAM上存储全部的元数据。且单台机器也无法有足够的计算能力服务多个Client。很显然,谷歌替换了GFS,将一个主服务器换为了多台服务器。
lec4 VMware FT
Q:介绍中说,要确保在物理服务器上确定性执行比在VM上确定性执行更困难, 为什么呢?
确保确定性在VM上更容易,因为虚拟机管理程序会仿真和控制硬件的许多方面,这些方面在主要执行和备份执行之间可能会有所不同,例如中断交付的准确时间。
Q:什么是hypervisor?
hypervisor是虚拟机的一部分,它类似于VMM(Virtual Machine Monitor),hypervisor模拟为一台电脑,guest操作系统运行在这个模拟的电脑中,guest运行的这个模拟计算机一般被称为虚拟机,在这篇paper中,primary和backup都是运行在虚拟机中的guest操作系统。FT是hypervisor实现每一个虚拟机的一部分。
Q:GFS和VMware FT都提供了容错,我们该如何考虑何时哪一个更好?(How should we think about when one or the other is better?)
FT复制计算;你可以使用它来透明的添加容错对于任何一个现存的网络服务。FT提供了公平的强一致性,而且其对于客户端和服务器都是透明的。你可以使用FT使得现存的邮件服务得到容错的效果。GFS是完全相反的,它只对于存储提供了容错,GFS被专门设计于特定的简单服务(存储),它的复制远比FT有效率。例如,GFS不需要使得中断指令在每个副本发生的位置完全一致,GFS通常用于一个大型系统中实现容错服务,比如FT本身的容错依赖于一个primary和backup共享的磁盘,你可以使用像GFS这样容错的服务来实现(笔者:类似于一个分布式锁就可以)。
Q:3.4节的bounce buffer如何帮助避免竞争?
当网络数据包或请求的磁盘块到达主要节点并需要复制到主要节点的内存中时,就会出现问题。如果没有bounce buffer的话,相关的硬件会在软件执行的时候把数据复制到内存中去。这会使得primary和backup看到中断指令时间有细微的差别,这就出现了分歧。
最后的效果是这个网络数据报和磁盘阻塞精确的在同一时间出现在primary和backup上,它们读到的都是相同的数据。
Q:什么是“共享磁盘中原子的 test-and-set 操作”
这个解释写了一大堆。其实就是一句话:一个无阻塞的分布式加锁请求,返回true代表拿到锁,返回false带便未拿到锁。文中还附带了这个共享磁盘的伪代码:
test-and-set() {
acquire_lock()
if flag == true:
release_lock()
return false
else:
flag = true
release_lock()
return true
}
Q:遵循outpu rule将会损失多少性能?
表2中提供了一些数据,遵循ouput rule,传输效率降低,但幅度不大。
Q:如果应用程序调用随机数生成器怎么办?这会导致产生不同的结果并导致primary和backup执行结果不同吗?
不会,所有数据的随机性都由hypervisor控制,例如应用使用当前时间,硬件周期计时器,或者精确的中断时间作为随机来源。这三种情况下hypervisor会拦截primary和backup之间有关的指令,并确保它们产生相同的值。
Q:如果主服务器在将输出发送到外部后立即发生故障,会发生什么情况?
backup可能会在成为primary以后重复输出,因此输出可能会产生两次。这个重复对于网络和磁盘I/O来说并不是一个问题,如果输出是一个网络数据包,然后接收方的TCP协议会保证自动丢弃(TCP保证处理重复),如果输出事件是磁盘I/O也不必担心,因为其是幂等的。(写相同的数据到相同的位置,其没有中间I/O)
Q:backupFT如何在备用指令流中的特定点(即在最初发生在主节点上的中断所在的同一条指令)处传递中断?
很多CPU的特性,即让VMM告诉CPU一个指令,CPU将会在一些指令以后执行中断。
Q:只提出fail-stop(网络分区,宕机)这个错误是否合理?还有没有其他类型的错误?
是合理的,很多实际世界的错误都可以看做fail-stop,例如网络和宕机。想要做的更好的话我们要去防止看起来正确但实际运行错误的事情,最坏的情况下,我们可能会遭到恶意的攻击者,其实说的就是拜占庭错误,在6.824中绝大多数时候只讨论fail-stop。
lec6 Raft1
Raft这一节问题太多,筛一些主要说Raft的。
Q:Raft是否为了简单牺牲了一切?
Raft为了清晰放弃了一部分性能;例如:
- 每一步操作都需要写入磁盘,
- There can only usefully be a single AppendEntries in flight from the leader to each follower: followers reject out-of-order AppendEntries, and the sender’s nextIndex[] mechanism requires one-at-a-time. A provision for pipelining many AppendEntries would be better.(个人认为这个观点所说的问题都可以解决,因为AppendEntries是可以一次发送多条日志的,而且日志落后通过nextIndex[]机制来找匹配点也可以优化,且出现概率不高。)
- 快照的设计只适用于较小的状态,因为它会把所有的状态写入磁盘,如果状态非常大的话,你需要一种只写入最近状态的方法。
- 同样,通过向复制副本发送完整的快照来使恢复的副本保持最新状态会很慢,这是不必要的,因此副本已经具有旧的快照。但是假设副本的快照版本低于primary的快照,我们也没办法只发送一部分状态的更新,因为那部分日志已经丢弃了。
- 服务器可能无法充分利用多核,因为必须一次一次执行操作(按日志顺序)。
可以通过修改Raft来解决这些问题,但结果作为教程的价值可能较小。
Q:在现实世界的应用中,Raft的性能与Paxos相比如何?
在paper中提到,最快的Paxos衍生协议可能比Raft更快。在一些情况下Raft的elader表现并不好,如果一个数据中心的副本和客户端距离比较远,则使用派生自原始的Paxos协议更好,原因是Paxos是没有leader的,客户可以喝本地数据中心的副本进行对号,而不必和远程的领导人进行对话。
Q:如果只有少数集群处于活动状态,是否存在像Raft这样的系统可以生存并继续运行?
这里教授给出了两种方法:
If somehow clients and servers can learn exactly which servers are live and which are dead (as opposed to live but unreachable due to network failure), then one can build a system that can function as long as one is alive, picking (say) the lowest-numbered server known to be alive.
However, it’s hard for one computer to decide if another computer is dead, as opposed to the network losing the messages between them. One way to do it is to have a human decide – the human can inspect each server and decide which are alive and dead.
The other approach is to allow split-brain operation, and to have a way for servers to reconcile the resulting diverging state after partitions are healed. This can be made to work for some kinds of services, but has complex client-visible semantics (usually called “eventual consistency”). Have a look at the COPS, FuzzyLog, and Bitcoin papers which are assigned later in the course.
大概的总结一下,就是使用分布式锁和允许脑裂。
Q:在Raft中,正在复制的服务在选举过程中对客户端不可用。 在现实世界中,这会导致多少问题?
用户可见的停顿时间大概是十分之一秒,且预期的fail-stop是很少的,所以不会造成什么问题。
Q:是否有其他没有领导人停顿的共识系统?
有些基于Paxos的版本没有领导者或选举,这样我们就不会遭受到选举的停顿了,相反的,任何服务器都可以有效的充当leader。这样没有leader的花费就是在每一次的一致中都需要更多的数据传递。
Q:如果一个客户发送了一条消息,但是leader在把消息发送给follower以前就宕机了,新的leader也没有接收到这条日志,这个消息会丢失吗?
会的,因为Client并没有被告知消息已经写入成功。这里还有一个问题即如果新leader收到了这个log呢?(5.4.2)又分两种情况,即新leader把一条新的日志已经commit,此时上一条日志遵循日志匹配原则,也会被写入。如果新leader在新的任期内还没commit任何一条又宕机,且另一个新leader提交了一个不同的日志,最早的那个日志就要被删除了。这里我们可以看到任何一条日志可能写入成功也可能失败,所以我们的系统有必要进行判重。
Q:leader会等待AppendEntries RPCs的回复吗?
一般情况下是并发的对每一个follower发出AppendEntries(goroutine),然后在收到以后触发回调。但是这里我有一个问题,就是如果leader收到了两个请求,假设为A,B,A先发出,B后发出,那么在log中A在B前面,但是B先收到大多数,此时虽然A没有收到大多数,但是在leader看来它仍然被提交?
Q:对于在Raft协议之上构建的应用有什么限制吗?
不清楚教授说的重复请求是什么意思,如果是单纯的重演日志保证这些像 VMware FT 中Client一样没有输出不就好了,所说的replication优势什么意思呢?
I think that in order to fit cleanly into a replicated state machine framework like Raft, the replicated service has to be self-contained – it can have private state, and accept commands from clients that update the state, but it can’t contact outside entities without special precautions. If the replicated application interacts with the outside world, the outside world has to be able to deal correctly with repeated requests (due to replication and replay of logs after reboot), and it has to never contradict itself (i.e. it has to be careful to send exactly the same answer to all replicas, and to all re-executions of log entries). That in turn seems to require that any outside entity that a Raft-based application contacts must itself be fault-tolerant, i.e. probably has to use Raft or something like it. That’s fairly limiting.
As an example, imagine a replicated online ordering system sending credit card charging requests to some external credit card processor service. That external processor will see repeated requests (one or more from each replica). Will it respond exactly the same way to each request? Will it charge the credit card more than once? If it does do the right thing, will it still do the right thing if it crashes and
reboots at an awkward time?
lec7 Raft2
Q:当Raft接收到一个读请求的时候时候会提交一个空操作?
第8节中提到了两种方法。leader可以先发送给每一个Client一个心跳包,以获取最新的提交记录;这样可能会有风险,即这个leader已经不是leader了,我们可以维护一个lease,保证这段时间内不会出现第二个leader,以此保证正确性。(正确性依赖于不同机器上的时间,危险)
Q:为什么要提交一条空日志?
因为一个新的leader并不知道它的最后几个日志条目是否已经提交过,就像这张图中的c,如果S1没有宕机,也没有写入4,这条空日志会让其他服务器看到2.
Q:如何使用心跳机制去提供租约操作?为什么依赖时间保证安全性?
论文(8)中没有给出具体实现,但是我觉得可以在心跳包中增加一个租约机制,即在收到这条租约后一段时间内不会出现哪怕此leader宕机也不会出现第二个leader这样就保证了读取不会出现脏数据。第二个问题是因为时钟偏移,可能导致两台机器同一个时间的执行市场不同,这样算法就无法保证不会出现第二个leader了。
Q:创建快照时,数据和状态是否已经被应用于客户端?如果这是客户的数据,那么除了paper中提到的修改以外还需要客户程序的支持吗?When snapshots are created, is the data and state used the one for the client application? If it’s the client’s data then is this something that the client itself would need to support in addition to the modifications mentioned in the raft paper?
如果创建了一个KV服务使用Raft进行容错,那么这个服务将会有一个模块存储着一张表,表中的key和Value就保存在快照中。
Q:paper中说道”if the follower receives a snapshot that describves a prefix of its log, then log entries covered by the snapshot are deleted but entries following the snapshot are retained“,这是否意味着我们可以在状态机中进行删除操作?
状态不会丢失,因为如果快照导致删除日志,那么快照一定包含了这些日志中所有的状态。
Q:快照的对于一系列更新操作分布在几个键上很有用。如果一系列更新插入了不同的key,在这种情况下会节省成本吗?
这样可能快照的意义不是很大。但是可以从另一个角度看问题,可能快照的组织形式在数据访问上相比于日志更简单,举个例子:在一个排序的表中,可能快照从崩溃(crash-reboot)中恢复要快于日志(必须排序)(持久化中包含log)。但是这种情况其实是非常特殊的,因为一般来说日志都大于状态。
Q:InstallSnapshot是否会带来昂贵的带宽花费?
是的,在状态很大的情况下(比如一个数据库)。但这并不是一个容易解决的问题。您可能希望领导者保留足够的日志,以覆盖跟随者滞后或暂时离线的所有常见情况。你可能希望有一种方法仅仅传递服务器之间的不同之处。(举个例子,仅传递最近被修改的部分)
Q:是否需要担心因为要把大量的log中数据写入到快照而使得时间花费超过选举超时?Is there a concern that writing the snapshot can take longer than the election timeout because of the amount of data that needs to be appended to the log?
这在大型系统中确实是一个潜在的问题,例如你需要拷贝一个十亿字节的数据,但是你的磁盘每秒只能写一亿字节,这样写一个快照需要十秒。一个可能的方法是在后台进行写入(安排不等待写入,可能可以在子进程中执行),而且确保快照创建的频率不低于每十秒一次。
Q:哪种情况下follower会接受到自己日志的前缀?
网络分区可能使得日志乱序,且RPC系统也可能会无序执行,,所以举个例子,如果leader发送了index到100的快照,随后又发送了index为110的日志,但因为网络原因后一条先到(这里使用的是RPC进行通信而不是TCP)
Q:如果follower收到的快照是其日志的前缀,然后替换到该点为止的日志中的条目,则该点之后的条目是领导者不知道的条目吗?
不一定, 在快照的发送被网络延迟以后。follower可能有一些日志条目是快照中没有的;或者leader发送了日志但并没有提交的的时候。
Q:当leader发送的 InstallSnapsho t命令是 follower log 的前缀的时,而且我在之前已经进行了压缩日志,是否可以安全的认为我的日志已经包含了较小的日志?
接受者收到小于日志条目的快照时忽略是正确的选择,在lab3中将会看到这个例子,例如RPC由于网络分区出现的乱序。
Q:leader如何确定哪一个服务器出现落后需要发送一个快照?
如果一个follower拒绝了一个index为i1的AppendEntries由于#2或者#3(论文中的图2,描述AppendEntries那里),而且leader已经丢弃了i1之前的数据,leader就会发送一个InstallSnapshot而不是follower发来的nextIndex[]后的值。
Q:在实际使用Raft的场景中,快照的发送有多频繁?
I imagine people using Raft would tune it so that snapshots were rarely needed (e.g. by having leaders keep lots of log entries with which to update lagging followers).(个人认为实在数据累积过多的时候leader才可能会产生快照来减小内存使用,从而才可能导致发送快照)
Q:InstallSnapshot是原子的吗?如果一个服务器在安装快照进行到一部分的时候宕机,leader重新发送一个 InstallSnapshot RPC,它像 RequestVote 和 AppendEntries 一样是幂等的吗?
InstallSnapshot必须实现为原子,leader重新发送快照是无害(harmless)的。
Q:写时复制如何解决创建快照的性能问题?(7的最后一段)
基本的思想是让服务器派生到fork,给子进程一个在的内存状态的拷贝,如果fork确实复制了所有内存,并且状态很大,这将会很慢,但是很多操作系统不会在fork时拷贝所有的内存,而是把页标记为"copy-on-write",使得这些页在父子进程间只读,然后,如果任何一个试图写入页面,操作系统将看到页面错误,并且操作系统将仅在该点复制页面。最终结果通常是,子进程在fork()时看到其父进程内存的副本,而执行相对较少的拷贝。且这样父进程新的更新就可以被接收而不影响到快照。
Q:什么样的压缩方案(VIZ,ZIP,Huffman)对于Raft的快照来说最有效?
取决于服务器要存储的数据,如果存储的是图片的话,使用JPEG是个不错的选择。如果你认为快照可能共享了一部分前面快照中的内容,你可以使用一些树结构去压缩。(数据重复太多可以只用ZIP,内部以LZ算法为基础,对大规模重复的数据有特效)。
Q:论文中指出有很多自主实现的Raft,是否有任何改进建议可以纳入该算法的修订版中?
https://www.cl.cam.ac.uk/~ms705/pub/papers/2015-osr-raft.pdf
zookeeper
Q:为什么写需求是强一致性而读不是这样的?
作者需要高的读吞吐量,所以希望副本能够在不涉及leader的情况下满足客户的读请求。一个副本可能不知道已经提交数据(没在那个大多数中),或者是知道写入了,但是不知道是否提交。因此副本的状态可能落后于其他副本和leader,所以读可能返回落后的数据。所以通过记录zxid满足 FIFO Client order。
Q:paper中的 pipelining 是什么意思?
这里有两个角度可以说:
- zk的leader为了能在网络和磁盘的发送汇总更有效率,把多个客户端的请求合并(batche)在了一起。对于磁盘和网络来说,通常一次发送多条小消息要比分成一次发送一条消息更有效率。这中效率的提升只发生在leader在同一时间看到了多个Client请求,这依靠于leader有一些活跃的客户端。
- ZooKeeper使每个客户端可以轻松地一次保持许多未完成的写请求,以支持异步操作。在Client的角度来看的话,它可以发送大量的请求而不必等待响应(会作为commit的通知稍后到达)。在leader的 角度来说,Client的给leader带来了很多的请求,可以看做一个大的,更有效的批次处理(that client behavior gives the leader lots of requests to accumulate into big efficient batches)。
- 对于pipelining的一个担心是可能在操作消息发送的过程中出现乱序,这将会导致paper2.3中的问题。如果leader在一个准备好的写操作前执行了很多写操作,你一定不想看到这些操作乱序,因为Client可能会在上一个写操作就绪前去观察(If a the leader has many write operations in flight followed by write to ready, you don’t want those operations to be re-ordered, because then other clients may observe ready before the preceding writes have been applied.我对这段话的理解就是要保证FIFO Client order,用户读操作希望看到最后的写入值,而不是乱序的值)。为了确保这不会发生,zk保证用户的操作是FIFO的,即客户端操作按其发出的顺序被应用,这也是通道的作用。
- 以上是翻译,下面是我的看法:我对这个东西的理解有两点,一是合并请求,二是保证FIFO,当然这个顺序(写请求)是在leader维护的,即多个Client之间并发,顺序无关,单个Client保证顺序。
Q:paper 中的 wait-free 是什么意思?
这是精确的定义:并发数据对象的 wait-free 实现是一个保证进程都可以在有限的步骤内完成某些操作的方法,且于其他进程的执行情况无关。(A wait-free implementation of a concurrent data object is one that guarantees that any process can complete any operation in a finite number of steps, regardless of the execution speeds of the other processes.)
zk是wait-free是因为它可以处理一个Client的请求而不需要等待其他Client做什么。This is partially a consequence of the API(不知道怎么翻译),尽管zk被设计来用于支持客户端与客户端之间的协调和同步,但是zk却不存在要求一个客户端等待另外一个客户端这样的API。相反的,如果一个系统支持acquire lock操作,但需要等待持锁者release,那么这个系统就不是无等待的。
但其实zk的通常需要clients互相等待,而且zk也确实提供了一种等待机制,即watchs。wait-freedom API 最主要的影响是 watches 被从其他的操作中分离出来。watches和 atomic test-and-set updates 的结合将会允许Client合成更为复杂的阻塞抽象。
Q:leader如何知道客户端一系列异步执行的操作的顺序呢?
paper中并没有提到,但是解决的方案也许是这样:客户端为异步操作请求编号,leader将会跟踪每一个Client它们期待的下一个序号。领导者无论如何都必须保持每个会话的状态(用于客户端会话超时),因此跟踪每个会话的请求序列号可能不需要多少额外的工作。 当领导者发生故障并且另一台服务器接管该信息时,必须保留此信息,因此客户端序列号可能会在复制的日志条目中传递。(我个人认为会在leader中维护一个全局的zxid,且保证单个Client的请求序列一定是有序的)
Q:使用 ‘fuzzy snapshots’ 的原因是什么?(4.3)
一个精确的副本可能在某一个特殊的日志点之前都是一致的;这个快照将会包含这个点之前的所有写入,且不会包含这个点之后的所有写入。而且需要非常清楚在使用一个快照进行恢复的时候从哪里开始重放日志。但是,创建精确的快照需要一种方法来防止在创建快照并将其写入磁盘时发生任何写操作。在快照期间阻塞写操作可能会非常影响性能。(笔者注:这里我任务可以采取Raft中对于快照的处理方法,即使用fork来避免这个问题)。
zk的 fuzzy snapshots 的要点是,ZooKeeper从其内存数据库创建快照,同时允许写入数据库。这意味着快照并不对应于某个日志中的特殊点,这个快照包含了与快照创建并发写入的数据集。在重启以后,zk通过重放所有的日志条目来恢复一致性状态,因为zk的更新的操作是幂等的,且会以相同的操作交付,所以在重启和重放日志以后应用程序的状态是正确的(有些消息可能会被应用两次)。
zk的leader将 Client API 操作转化成幂等事务。例如,Client发送了一个附带条件的setData,且版本号于请求匹配,zk将会创建一个setDataTXN,其中包含新数据,即新版本号和更新的时间戳。这个事务是幂等的,zk可以执行它两次,且会造成相同的状态。(这个转化的步骤我认为也可以理解为pipelining做的事情,也就是把Request Processor也看做pipelining的一部分)
Q:zk如何选主?
使用ZAB算法,是一个原子广播协议,其内置了选举,很像是Raft,这是zab的paper。
Q:zk的性能在相比于其他系统(例如基于Paxos)时如何?How does Zookeeper’s performance compare to other systems such as Paxos?(个人认为这个问题有点问题,zk是个组件,paxos是个算法,没有可比性啊。)
zk具有令人印象深刻的性能(特别是吞吐量),三个zk服务器每秒可以处理21000次的写入,而Raft每秒只能处理数十次的操作(存盘的话),使用SSD(固态硬盘)可以达到每秒数百次。
Q:zk的数据库有多大,看起来zk需要记录很多的数据。(It seems like the server must have a lot of memory)
这取决于应用,不幸运的是,paper并没有提及作者在这方面的经验。因为zk被用作配置和协调,并不作为一个真正意义上的数据库,所以把数据存放在内存中看起来是合理的。例如,你可以使用zk作为像GFS的master,然后用其他设备齐全的服务器做数据处理( For example, you could imagine using Zookeeper for GFS’s master and that amount of data should fit in the memory of a well-equipped server, as it did for GFS.)。这里我的理解是把zk的数据存储看做像GFS一样,把元数据存放在 zk master 中(毕竟一个节点只有1MB大),大量的数据存放在其他的服务器,类似与GFS的chunk。
Q:Client怎么知道如何离开barrier(2.4 第七个例子)
离开barrier涉及到watch所有其他参与到barrier的znodes。每一个Client等待所有其他的Client离开,如果全部都离开了,就会离开这个barrier进行其他计算。(笔者注:创建一个barrier需要先创建一个znode,参与的Client放置到这个znode下面。还有,我认为这个double barrier类似与count down latch,就是还在end处等待,这也许是它叫double barrier的原因吧)。
Q:是否可以在zk不进行服务下线的情况下添加更多的服务器到现有的zk服务集群中?
尽管在最初论文中集群的关系是静态的,现在zk已经支持“动态配置”了,这篇paper描述了这个机制。
Q:客户端如何实现watches?
取决于具体实现,在很多的例子中,可以注册一个回调,用于在watches被触发时调用。例如,在go的zk客户端中,通过“GetW()”来实现,当watch事件被触发时,事件将会被发送到这个channel中,应用可以使用select字句来检查这些channel。