这篇博文是探索三个分布式、一致性键值数据存储软件性能的系列文章中的第一篇:etcd、Zookeeper和Consul,由etcd团队所写,可让咱们全面地了解如何评估三个分布式一致存储软件的性能。翻译过程当中不免有偏差,还请你们谅解html
许多现代分布式应用程序都创建在分布式一致键值存储之上。Hadoop
生态系统中的应用程序和“Netflix栈”的许多部分都使用Zookeeper
。Consul
公开了服务发现和运行情况检查API
,并支持Nomad
等集群工具。Kubernetes
容器编排系统,MySQL
的Vitess
水平扩展,Google Key Transparency
项目以及许多其余系统都是基于etcd
构建的。有了这么多关键任务集群,服务发现和基于这些一致键值存储的数据库应用程序,测量可靠性和性能是相当重要的。java
理想的键值存储每秒摄取许多键,快速持久并确认每次写入,并保存大量数据。若是存储没法跟上写入,那么请求将超时,可能会触发故障转移和停机。若是写入速度很慢,那么应用程序看起来很慢。若是数据过多,存储系统可能会爬行甚至没法运行。git
咱们使用dbtester来模拟写入,并发现etcd
在这些基准测试中优于相似的一致分布式键值存储软件。在较低的层面上,背后的架构决策能够更加统一和有效地利用资源,这些决策转化为在合理的工做负载和规模下可靠的良好吞吐量、延迟和总容量。这反过来又有助于使用etd
的应用程序,如Kubernetes
,可靠、易于监控和高效。github
性能有不少方面,本文将深刻介绍键的建立,键值的填充和存储,来讲明底层的机制。数据库
在跳到高级性能以前,首先经过资源利用率和并发性突出键值存储行为的差别是有帮助的,写操做为验证这个问题提供了一个很好的例子。写操做必须和磁盘关联起来,由于写操做会持久键值到媒体。而后,这些数据必须跨服务器进行复制(replicate across machines
),从而致使集群间大量网络流量。这些流量构成了处理写的所有开销的一部分,这会消耗CPU
。最后,将键放入存储区直接利用内存获取键用户数据,并间接用于簿记(book-keeping
)。apache
根据最近的一项用户调查,大多数etcd
部署都使用虚拟机。为了遵照最多见的平台配置,全部的测试都在谷歌云平台计算引擎虚拟机(Google Cloud Platform Compute Engine virtual machines
)上运行,而且使用Linux OS
(全部虚拟机都有Ubuntu 16.10
, Linux
内核4.8.0-37-generic
和ext4
文件系统。咱们选择Ubuntu
做为一个中立的Linux
操做系统,而不是像Container Linux
那样的CoreOS
项目,以说明这些结果应该适用于任何类型的Linux
发行版。可是,在容器Linux
上收集这些基准测试将获得很是类似的结果)。每一个集群使用三个VM
,足以容忍单个节点故障。每一个VM
都有16
个专用的vcpu
、30GB
内存和300GB SSD
,能够达到150 MB/s
的持续写操做。这个配置足够强大,能够模拟来自1000
个客户机的流量,这对于etcd
的用例和如下资源度量所选择的目标来讲是最小的。全部的测试都进行了屡次试验,在运行之间的误差相对较小,不影响任何通常结论。etcd
的使用以下图所示:后端
键值存储基准测试设置缓存
全部基准测试都使用如下软件配置:服务器
软件名称 | 版本 | 编译语言版本 |
---|---|---|
etcd | v3.1.0 | Go 1.7.5 |
Zookeeper | r3.4.9 | Java 8 (JRE build 1.8.0_121-b13) |
Consul | v0.7.4 | Go 1.7.5 |
每一个资源利用率测试都会建立一百万个具备1024字节值的惟一256字节键(one million unique 256-byte keys with 1024-byte values
)。选择键长度以使用共同的最大路径长度对存储软件施加压力,选择值长度是由于它是protobuf
编码的Kubernetes值的预期平均大小。虽然精确的平均键长度和值长度是与工做负载相关的,但长度表明极端之间的权衡。更精确的敏感性研究将为每一个存储软件提供更多关于最佳案例表现特征的看法,但风险更大。网络
写操做必须持久化到磁盘,他们记录共识提案(consensus proposals
),压缩旧数据,并保存存储快照。在大多数状况下,写入应该以记录共识提案为主。etcd
的日志(log)将protobuf
编码的提议流转换为一系列预分配文件,在页面边界处同步,每一个条目都有滚动的CRC32
校验和。 Zookeeper
的事务日志(transaction log)相似,可是使用Adler32
进行jute
编码和校验和。Consul
采用不一样的方法,而是记录到boltdb/bolt后端,raft-boltdb。
下图显示了扩展客户端并发性如何影响磁盘写入。正如预期的那样,当并发性增长时,ext4
文件系统上/proc/diskstats
上的磁盘带宽会增长,以应对请求压力的增长。etcd
的磁盘带宽稳定增加,它写的数据比Zookeeper
还多,由于除了日志外,它还必须写boltDB
。另外一方面,Zookeeper
会由于写入完整的状态快照而丢失数据速率,这些完整的快照与etcd
的增量和并发提交相反,后者只写入更新,而不会中止全部正在进行的操做(stopping the world
)。Consul
的数据率最初大于etcd
,这多是因为在B+
树中删除了提交的raft
协议(raft proposals
)而致使的写放大,而后因为花了几秒钟写快照而出现波动。
建立一百万个键时的平均服务器磁盘写入吞吐量
网络是分布式键值存储的中心。客户端与键值存储集群的服务器进行通讯,集群中的服务器相互通讯。每一个键值存储都有本身的客户端协议,etcd
客户端使用创建在HTTP/2之上的Protocol Buffer v3 gRPC协议,Zookeeper客户端使用自定义的流式TCP
协议Jute,Consul
使用JSON
。一样,每一个协议都有本身的TCP
服务器协议。etcd peer stream protobuf编码的raft RPC提议,Zookeeper将TCP流用于有序的双向jute编码ZAB通道,Consul发布用MsgPack编码的raft RPC。
下表显示了全部服务器和客户端的总网络利用率。在大多数状况下,etcd
具备最低的网络使用率,除了Consul
客户端接收的数据略少。这能够经过etcd
的Put
响应来解释,其中包含带有修订数据的标题,而Consul
只是以明文true
响应。Zookeeper
和Consul
的服务器间流量多是因为传输大型快照和节省空间的协议编码较少。
使用1,000个客户端建立一百万个键时传输的总数据量
即便存储和网络速度很快,集群也必须当心处理开销。浪费CPU
的机会比比皆是:许多消息必须被编码和解码,糟糕的并发控制能够对抗锁定,系统调用能够以惊人的频率进行,而且内存堆能够捶打。 因为etcd
、Zookeeper
和Consul
都但愿leader
服务器节点处理写入,所以较差的CPU
利用率能够轻松下降性能。
下图显示了在扩展客户端时使用top -b -d 1
测量的服务器CPU
利用率。etcd CPU
利用率按预期平均和最大负载进行扩展,随着更多链接的增长,CPU
负载依次增长。最引人注目的是Zookeeper
的平均跌幅为700
,但客户端数量增长了1000
,日志报告太忙以捕捉,跳过其SyncRequestProcessor
,而后建立新的日志文件,从1,073%
利用率到230%
。 这种降低也发生在1,000
个客户端,但从平均值来看不太明显,利用率从894%
上升到321%
。一样,处理快照时Consul CPU
利用率降低10
秒,从389% CPU
降至16%
。
用于在客户端扩展时建立一百万个键的服务器CPU使用
当键值存储设计为仅管理元数据大小的数据时,大多数数据能够缓存在内存中。维护内存数据库能够提升速度,但代价是过多的内存占用可能会致使频繁的垃圾回收和磁盘交换,从而下降总体性能。当Zookeeper和Consul在内存中加载全部键值数据时,etcd
只保留一个小的驻留内存索引,直接经过boltdb中的内存映射文件支持其大部分数据,仅将数据保存在boltDB
中会因请求分页而致使磁盘访问,但整体而言,etcd
更好地考虑操做系统设施。
下图显示了在集群总内存占用量中向集群添加更多键的效果。最值得注意的是,一旦存储系统中有大量的键,etcd
使用的内存量不到Zookeeper
或Consul
的一半。Zookeeper
位居第二,占据了四倍的内存,这符合仔细调整JVM
堆设置的建议(recommendation)。最后,尽管Consul
使用了etcd
所用的boltDB,但它的内存存储(in-memory store)否认了etcd
中的占用空间优点,消耗了三者中最大的内存。
建立一百万个键时的服务器内存占用
随着物理资源的肯定,重点能够回归到聚合基准测试。首先,为了找到最大键提取率,系统并发性可扩展到一千个客户端。这些最佳摄取率为测量负载下的延迟提供了基础,从而衡量总的等待时间。一样,每一个系统客户端以最佳摄取速率计数,当密钥从一百万个键扩展到三百万个键时,能够经过测量吞吐量的降低来强调总容量。
随着愈来愈多的客户端同时写入集群,理想状况下,在提高以前,提取率应该稳定上升。可是,下图显示在写出一百万个键时缩放客户端数量时不是这种状况。 相反,Zookeeper
(最大速率为43,458 req/sec
)波动很大,这并不奇怪,由于它必须明确配置为容许大量链接。Consul
的吞吐量(最大速率16,486 req/sec
)能够很好地扩展,但在并发压力下会下降到低速率。etcd
的吞吐量(最大速率34,747 req/sec
)整体稳定,随着并发性而缓慢上升。最后,尽管Consul
和Zookeeper
使用了更多的CPU
,但最大吞吐量仍然落后于etcd
。
随客户端规模建立一百万个键的平均吞吐量
鉴于存储系统的最佳吞吐量,延迟应该是局部最小且稳定,排队效应将延迟其余并发操做。一样,理想状况下,随着键总数的增长,延迟会保持低且稳定,若是请求变得不可预测,则可能存在级联超时,抖动监视警报或故障。然而,经过下面显示的延迟测量来判断,只有etcd
具备最低的平均等待时间和规模上的紧密、稳定的界限。
etcd,Zookeeper和Consul键建立延迟位数和范围
Zookeeper
努力为并发客户提供最佳吞吐量,一旦它触发快照,客户端请求就会开始失败。服务器记录列表错误,例如Too busy to snap, skipping, fsync-ing the write ahead log
和fsync-ing the write ahead log in SyncThread: 1 took 1,038 ms which will adversely effect operation latency
,最终致使leader
节点丢失,Exception when following the leader
。客户端请求偶尔会失败,包括zk
等错误,例如zk: could not connect to a server
和zk: connection closed
错误。Consul
报告没有错误,尽管可能应该,它经历了普遍的差别降级性能,多是因为其大量写入放大。
凭借最佳平均吞吐量达到100万个键的最大并发性,能够在容量扩展时测试吞吐量。下图显示了时间序列延迟,以及延迟峰值的对数标度,由于键被添加到存储中,最多可达300
万个键。在大约50
万个键以后,Zookeeper
和Consul
延迟峰值都会增加。因为高效的并发快照,etcd
没有出现尖峰,可是在一百万个键以前略有延迟。
特别值得注意的是,就在两百万个键以前,Zookeeper
彻底失败了。其追随者落后,未能及时收到快照,这代表领导者选举须要长达20
秒才能锁定集群。
建立300万个键时的延迟
在建立一百万个或更多键时,etcd
能够比Zookeeper
或Consul
稳定地提供更好的吞吐量和延迟。此外,它实现了这一目标,只有一半的内存,显示出更高的效率。可是,还有一些改进的余地,Zookeeper
设法经过etcd
提供更好的最小延迟,代价是不可预测的平均延迟。
全部基准测试都是使用etcd
的开源dbtester生成的。任何但愿重现结果的人均可以得到上述测试的测试用例参数。对于更简单,仅限etcd
的基准测试,请尝试使用etcd3
基准测试工具。
编译自:Exploring Performance of etcd, Zookeeper and Consul Consistent Key-value Datastores