(阿里云块存储团队论文获 FAST 2024最佳论文)
FAST全称为Conference on File and Storage Technologies,创办于2002年,是由美国高等计算系统协会(USENIX)和美国计算机学会操作系统专业组织(ACM SIGOPS)联合组织的聚焦存储领域的顶级国际会议,代表了计算机存储领域的国际最高水平。创办二十多年来,FAST 推动了如软硬件结合、RAID、闪存文件系统、非易失内存技术和分布式存储等多项存储相关技术的发展。
本次的最佳论文来自阿里云存储团队,详细阐述了过去十余年阿里云弹性块存储(EBS)的架构演变。为应对用户需求和硬件发展,块存储三代系统架构设计焦点从最初的简单可用性,转变到第二代架构的高性能和低成本,演进到第三代架构的对 IO执行效能极致优化,实现了阿里云企业级块存储ESSD百微秒级平均延迟和毫秒级长尾延迟,单块云盘达到百万IOPS和4GiB/s 吞吐,有力支撑数据库、搜索和推荐等众多性能敏感型业务。基于高速非易失性内存,阿里云块存储进一步推出ESSD PL-X产品,单块云盘可提供30 微秒平均延迟,高达三百万IOPS和12GiB/s吞吐,为用户提供更极致的存储性能。
(阿里云技术专家张伟东在FAST 2024上介绍阿里云存储技术)
作为阿里云存储的关键技术成果之一,本文将对这篇论文进行深度解读。
论文全称:
《What’s the Story in EBS Glory: Evolutions and Lessons in Building Cloud Block Store》
01:十二年磨一剑:三代架构演进,打造高性能、低成本的块存储!
过去的 12 年间,阿里云块存储一共演进了三代架构,每一代架构升级都给用户提供了更高的性能和更低的成本,同时我们通过不断优化高可用的架构设计,为块存储的稳定性保驾护航。
- 简单易部署的初代架构
初代架构采用计算存储分离的架构。以简单易部署为目标。
计算节点中我们部署了客户端(BlockClient),用户虚拟机(VM)中的盘(VD)通过 BlockClient 向后端存储集群发送读写请求。计算节点中的单用户云盘在后端集群中被分配给一个独立的块存储服务器(BlockServer),负责进一步以多副本形式转发到不同 Chunk 存储服务器(ChunkServer)中以实现数据的容灾。
初代架构在 IO 路径上采用了 多对一映射(云盘到存储服务器)和 原地更新(In-place Update)的设计。一个用户的云盘同一时间只会被一个 BlockServer 服务,每个 BlockServer 同时服务多个云盘。同时,BlockServer 通过将用户云盘的地址空间线性划分为相同大小的 Chunk(通常为 64 MiB 大小),通过 ChunkServer 以 Ext4 文件的形式存储在 HDD 中。对云盘地址空间中每个数据块的更改,BlockServer 直接将请求转发到对应 3 副本的 ChunkServer,由 ChunkServer 直接修改文件。
初代架构设计的选择促进了业务的快速上线,然而在性能和成本方面有所局限。多对一映射意味着盘的热点可能集中在存储集群的单节点中,性能也受到单节点的制约。原地更新方式使得数据压缩和纠删编码的部署难度变大,因为它们会改变数据块的大小,数据在压缩和纠删过后,无法以原地更新的方式再次写入。
- 高性能、低成本的第二代架构!
第二代系统架构基于初代架构进行了大幅度创新,通过地址空间分段、采用日志结构以及部署压缩和纠删编码技术,极大提高了盘的性能并降低了存储成本。
从二代开始,块存储的业务逻辑与文件管理逻辑分开,文件管理逻辑由 盘古分布式文件系统 进行管理,并成为阿里云多项存储业务(EBS,OSS,OTS 等)的底座。在块存储业务中,我们采用了全闪存(SSD)集群,并设计了闪存友好的系统架构。
第二代架构首先采用了地址分段设计(Disk Segmentation Design),每个用户的盘逻辑地址空间被线性划分为多个连续的SegmentGroup(通常为数百 GiB),包含多个Segment(代表数十 GiB 的地址空间)。SegmentGroup的地址空间到Segment的地址空间映射类似于RAID0,以提高读写并行度。采用地址分段后,BlockServer以Segment为粒度管理用户的盘。至此实现了用户盘更高的性能可扩展性,提高更高的性能。
进一步,第二代架构采用了日志结构设计(Log-Structured Design),盘古文件系统提供了可供追加写的文件类型,BlockServer 得以不以就地更新的方式去写数据,而是采用追加写的方式。用户的写首先经由地址分段设计机制映射到唯一的Segment上,由BlockClient发送到对应的 BlockServer。BlockServer将由BlockClient来的请求转换为追加写的请求,并维护类似 LSM-tree 结构作为索引。
为提供更便宜的块存储服务给用户,基于日志结构设计,第二代架构部署了压缩(Compression)和纠删编码(Erasure Coding)。通过后台转储服务,第二代架构得以将数据利用后端计算资源从三副本转换为压缩和纠删编码格式。同时,BlockServer 在数据写入时无需感知存量数据的位置,用户的服务完全不受影响。
第二代架构大幅提升了单盘性能并实现了成本的降低。基于第二代架构,阿里云块存储推出ESSD云盘,实现1百万 IOPS, 4GiB/s 吞吐,100us平均延迟的高性能企业级产品。然而,随着硬件架构的不断发展,SSD每 GB 价格的不断下降,块存储的成本逐渐从空间敏感型转向了流量敏感型。因此,第二代架构带来的流量放大已不可忽视。对于每个写请求,BlockServer由于不采用数据压缩/纠删编码技术,仍然以三副本形式写入到盘古中。在后台进行转储时,数据需要再次从盘古读取并以压缩和纠删编码形式重新写入。部署数据表明,该架构的流量放大高达 4.69 倍,这成为了限制成本继续降低的又一瓶颈。
- 极致 IO 效能的第三代架构
第三代系统架构基于第二代架构继续创新,通过引入融合写入引擎,进一步降低了流量的放大系数。同时,第三代架构卸载数据压缩到 FPGA 中,进一步降低 CPU 开销,实现极致的 IO 效能。
第三代架构引入了名为融合写入引擎的设计,并在用户的写路径上实现了在线的数据压缩和纠删编码。在融合写入引擎的设计下,不同用户的写请求数据,先由BlockServer先聚合到名为 JournalFile 的文件,然后持久化到盘古中。
这一设计的核心巧妙之处在于,将不同的低流量用户的写入在时间上聚合,从而使得在短时间内可以聚集得到压缩和纠删编码所需要的数据量(压缩单元和纠删编码单元通常需要数十KiB,而单个Segment的流量较小,通常不能在低延迟的场景下积累起足够多的数据量)。通过在用户的写路径上在线使用压缩和纠删编码技术,流量放大得到显著下降。
更进一步,BlockServer会在内存中以Segment为粒度积累每个用户的写请求数据,然后当数据量达到一定量时(通常为数MiB),BlockServer通过后台的Dump操作将数据以压缩纠删编码格式的 DataFile 持久化到盘古中。通过在聚合和Dump操作中都使用数据压缩和纠删编码,第三代架构的流量放大得以大幅下降。部署数据显示,流量放大相较第二代架构下降了66%。
JournalFile和DataFile虽然形式上都经过压缩和纠删编码,但其数据本身和用途却有很大差别。一方面,JournalFile中混合了来自不同用户和不同 Segment 的数据,因此JournalFile在大部分时间内是不会被读取的,除非BlockServer发生故障。另一方面,DataFile主要用来服务用户的读请求。因为DataFile是以Segment为粒度存储数据,因此在处理用户读请求时,从DataFile中读取数据的开销远小于从 JournalFile 读取。
通过融合写入引擎,BlockServer在用户写路径上实现了在线的数据压缩和纠删编码,但这些计算操作带来了额外开销。例如,使用CPU压缩一个 16 KiB的数据块,延迟将增加25us。 这一开销在平均延迟为100us的ESSD云盘中是不可接受的。
基于此,为避免在线数据压缩带来的额外延迟,我们将数据压缩卸载到定制的FPGA中。我们在FPGA中实现了一个调度器来将数据块拆分为固定大小的(例如,4 KiB)片段,并且利用多个执行单元并行地执行这些片段的 压缩/解压缩 任务。为确保数据完整性,我们在数据压缩链路中实现了端到端的CRC检查。在FPGA返回压缩数据后,BlockServer会立即解压数据,并通过 CRC 检查来验证数据完整性。
02:高可用架构助力块存储平稳运行
伴随着十二年高速发展,阿里云块存储不仅给用户带来高性能和低成本的块存储服务,也在生产过程中沉淀了丰富的高可用的架构设计。我们关注服务故障时的爆炸半径(即单一角色故障时影响的客户数目),并设计低爆炸半径的架构,来增强系统的稳定性。
- 控制面:分组 BlockManager
在第二代架构的最初设计中,控制面由三个部署在不同机器上的进程(BlockManager)组成。其中一个机器上的BlockManager作为主要角色服务单存储集群内所有的控制面请求。随着集群规模的扩大,单一节点需要管理的用户数目急剧增加,当该节点出现故障时,受到影响的用户数会增加。
(分组 BlockManager 的架构图)
在这样的背景下,我们设计了分组架构的控制面(Federated BlockManager)。分组架构中,服务控制面请求的角色被分拆到多个机器上,每个机器管理一组分区(Partition),其中每个分区管理一小组用户的盘。
同时,我们引入了轻量级的中心管理角色(CentralManager)。CentralManager 负责管理BlockManager的状态,以及对Partition在BlockManager之间进行均衡调度,不参与用户关键路径。
在分组架构的设计下,单一机器宕机对用户的影响急剧减少。举例而言,对于在百台数量规模的集群,在最初设计中,一个控制面机器的宕机会影响 10 万级的用户。在分组架构下,通过在每一台机器上部署一个BlockManager,单机宕机影响的用户范围下降到数千级别。更进一步,通过创建更小粒度的Partition(通常为百盘级)来维护盘的元数据,宕机时元数据的恢复速度也得以提升。单一机器宕机,CentralManager可以在多个BlockManager上并行加载盘的元数据,显著提高控制面的可用性。
- 数据面:逻辑故障域(Logical Failure Domain)
数据平面由一个存储集群内的多个 BlockServer 组成,每个BlockServer同时服务数千个 Segment 并处理这些Segment的I/O请求。当一个BlockServer崩溃时,控制平面会将其服务的所有Segment迁移到集群中的其他BlockServer上,以便 I/O可以快速在其他BlockServer上恢复。
然而,这种机制可能导致故障在BlockServer间迅速传播并造成级联故障。具体来说,如果崩溃是由异常的Segment(例如,错误的代码实现)引起的,迁移后,BlockServer会再次崩溃。在这种情况下优化Segment调度以加快恢复速度可能会导致更多的BlockServer在短时间内崩溃,甚至导致整个集群的服务异常。
根据生产经验,我们得到了 3 个重要观察:
故障通常源于单个云盘或Segment的请求,因此我们需要监Segment的状态而不是BlockServer。
故障的根本原因大多是软件错误(例如,代码Bug或错误的配置),因为硬件或机械故障不太可能使得故障随着Segment的迁移而扩散。由软件引起的故障可能需要大量的时间才能最终确定问题的罪魁祸首,更不用说构建自动恢复工具了。因此,更实际的方法是主动减少影响。
- 如果不加干预,级联故障可能会迅速传播到整个集群。作为一个分布式服务,我们可以容忍一些BlockServer服务异常,但是不能发生整个集群的宕机。
基于这些观察,我们设计了逻辑故障域。其核心思想是通过将可疑Segment分配到一组特定的BlockServer中来隔离它们,以避免其他BlockServer甚至整个集群受到影响。我们首先将每个Segment与一个最大容量为 3 的唯一令牌桶关联起来。每次Segment被迁移到新的 BlockServer 都会消耗一个令牌,并且令牌每 30 分钟重新填充一次。当令牌桶为空时,Segment的任何迁移只能从 3 个预先指定的BlockServer中选择一个,称其为逻辑故障域。
Segment-level故障域可以有效地隔离异常的Segment。然而,如果存在多个异常的Segment(例如,来自一VD)甚至是异常的云盘,Segment-level故障域不足以防止多个级联故障同时发生。
我们的解决方案是将故障域合并为一个。具体来说,当存在多个Segment形成一个故障域时,我们选择第一个故障域作为全局故障域,以确保最多只有 3 个BlockServer被隔离用于级联故障,而不会降低集群容量。任何后续的具有空令牌桶的Segment都将共享同一个全局故障域。在部署逻辑故障域后,我们成功地防御了由Segment迁移引起的多次潜在的级联故障。
03:结语
目前,阿里云在分布式存储领域已积累了一系列创新实践,并发表于计算机系统领域顶级会议上,包括自研分布式存储系统底座盘古(FAST 2023),面向云原生的大规模文件系统(FAST 2023),基于叠瓦式磁盘高性能存储引擎(FAST 2023),高性能分布式键值存储系统(SIGMOD 2021)。
(阿里云存储资深专家储道介绍阿里云盘古分布式存储系统论文)
以外,在高性能存储网络方面,自研的高性能用户态协议栈(ATC 2023),自研的高性能 RDMA 存储网络(SIGCOMM 2022),HPCC 流控算法(SIGCOMM 2019),RDMA 网络的大规模实践和优化(NSDI 2021),引领了云存储进入微秒延迟时代。前不久,阿里云分布式存储技术还获了中国发明专利金奖。
(阿里云分布式存储技术专利获中国专利金奖)
目前,相关技术已广泛应用到阿里云统一存储平台盘古分布式存储中,并成为阿里云提供存储服务的基础升级方案。所支撑的存储服务已广泛应用于铁路12306、云上奥运会、电子社保卡、医保平台、数字政府、城市大脑、杭州亚运等重要工程,为全球数百万客户提供服务,累计服务超9亿人次。