OIL将存储空间抽象化,并与分布式缓存系统VCache配合,降低了Facebook视频延迟的同时,并减少了存储与计算开销。感谢赵化强、李东明完成本文技术审校。
文 / Roberto J Peon, Rishit Shroff
译 / John
技术审校 / 赵化强、李东明
多年以来,开发者在编程时所秉持的基本思路是“open()ed”一个文件并“write()n”,随后在本地文件系统“read()”。但在分布式系统中,这些操作涉及分布在不同网络多个位置的主机上的多个进程。这些主机中的每一个 (或网络本身 )都可能独立且意外地出现宕机。存储系统应该如何应对此类故障,防患于未然?我们创建了一种输出输入语言——OIL,其原理是对异构存储系统中的文件进行统一的命名空间管理和抽象化。我们相信OIL代表了对通用存储抽象化的新理解。OIL VCache将这种抽象化过程与我们的分布式虚拟内存系统VCache相结合,可以显著的提升效率和性能。
大型公司通常使用许多不同的存储系统,并希望以此能够轻松访问和共享文件。但随着时间的推移,企业需要定期将数据迁移到不同的存储系统中。OIL使企业能够“配置”异构存储系统,而不用重新编码,这大大减少数据迁移的成本。与此同时,OIL还扩展了POSIX API以更好地察觉不同分布式系统之间的细微差别。通过提供易于使用的抽象化过程捕获这些所需的关键数据,OIL可以为编写应用程序的开发者以及维护存储系统的运维人员提供更多便利。
开发OIL
想象一下,你的一个应用正在写入数据到三台主机中,只有一台主机写入成功。那么I / O系统应该向用户返回“成功”——还是应该等待三台主机都“成功”、“失败”或“超时”才返回?这个问题的正确答案取决于应用场景的特殊性及其所需权衡的指标。例如,与家人进行视频会议时,其需要权衡的指标与执行在线购物交易有很大不同。特别是视频会议应用程序可能会通过舍弃部分一致性和数据丢失以换取低抖动和低延迟,而购物应用程序则恰恰相反。
通过组合的方式,每个应用可以共享存储系统,并且可以进行各自的优化。为实现此目的,会存储并理解一张有向无环图(DAG),此无环图可以表示任意文件的I/O流程。开发人员可根据每个文件甚至每个I/O的不同属性编写不同的DAG,并随着时间的推移与需求的变化更改这些属性,继而根据文件的使用情况(无论是经常访问还是很少访问数据)无需更改其名称即可匹配存储属性。这样使得OIL虚拟文件系统与文件能够一起移动并使它们保持原有的名称,而不是将文件从一个存储系统移动到另一个存储系统,继而使其名称出现变化。
除了通过DAG配置I/O外,OIL还对熟悉的read()API进行了优化。对于大多数系统,当所请求的数据超出当前文件结尾的字节时将产生越界错误。但是OIL的read()调用会阻塞,直到读取到至少一个字节或发生超时 - 即使请求的字节超出最大写入偏移量,也就是能够读取还未写入的字节。(由于不再需要对新数据进行轮询,因此其含义非常深刻。读取文件中的一系列偏移量本质上等价于订阅该范围的偏移量。)通过去掉低效的轮询方案,我们现在可以创建高效的通信系统。
虽然这种功能很强大,但经过一些实际的部署,我们意识到需要另一项调整。为了确保在已经写入完成的文件上的I / O不会产生出乎意料的延迟,OIL允许文件成为永久保持不变状态,这意味着文件的比特值永远不会改变(尽管存储的位置可能仍会改变)。它还确保OIL可以“read()”读取超过文件结尾时,立即返回越界错误。
OIL VCache
尽管OIL授权开发人员能够跨异构存储系统编写I / O,但我们意识到我们需要的不仅仅是OIL抽象化。正如虚拟内存改进了现代操作系统一样,分布式虚拟内存可以为分布式系统的I / O提供实质性的改进。
为了获得这些性能优势,我们创建了一个名为VCache的分布式虚拟内存系统。分布式内存的概念并不新鲜,但VCache与其他系统之间的差别在于集成了OIL。OIL访问VCache,VCache通过OIL访问其他存储。由于缓存(通常是多层)是实现最佳优化的重要部分,因此使用VCache(或多层VCache)组合存储可为开发者带来可观的益处。VCache存在于多个用户定义的位置并在每个位置提供了在空间、速度和可靠性之间权衡的机会。VCache支持各种写入模式,包括直写(write-through)、绕写(write-around)和不同类型的回写(write-back)。这些模式允许应用程序开发人员有效地借助缓冲,有效调用那些依赖存储于虚拟内存系统的数据。
OIL VCache如何工作?
OIL的DAG由存储模块和竞争节点组成。存储模块(例如本地文件系统或高速缓存)表示DAG中的各个节点。每个节点可以具有其特定实现某向功能的独立配置。除了向客户端传递数据或从客户端调用数据之外,DAG节点还可以向OIL框架传递其工作状态为满意(satisfied)或耗尽(exhausted)。除此之外,DAG节点可以将批量数据和地址空间变换附加到节点。转换包含前向纠错、压缩、加密和数据分块。节点之间的连线描述了传输数据时使用的属性,例如要设置的服务质量级别或使用的协议。
竞争节点是内置虚拟节点,可以包含任意数量的子节点。它允许配置最大并发、num-until-satisfied、num-until-exhausted和交错启动延迟。此节点允许描述if-then-else-chain、for-loop或parallel-for-loop之中的任何组合的控制流。当num-until-satisfied子节点声明“satisfied”状态时,竞争节点本身对其父节点声明“空闲”。当num-until-exhausted子句被执行并声明“耗尽”状态时,竞争节点本身会向其父级声明“耗尽”。由于节点可以在声明“耗尽”之前可选地声明“空闲”,明确发出信号并通知应用可以继续运行,以将用于特定I / O的所有工作完成的信令有效区分。
异步或委托写入的一个棘手问题是缓存的故障域与执行写入操作的主机的故障域不同。使用OIL VCache,开发者可以拥有本地缓存,这些缓存与写入主机具有相同的运行结果,同时还使用远程主机实现长期持久性和负载共享。使用这样的层次结构,开发者可以选择减少IOPS和存储系统开销,并且仍然放弃相对较少的期望属性,例如运行结果共享和对字节的最低延迟访问。
但是,当要写入的数据总和超过本地主机内存时,这种本地缓存将失败。由于VCache使用OIL作为后备存储,并且由于VCache可用于OIL,因此一个VCache实例可以使用另一个VCache实例作为其后备存储。这意味着开发者可以拥有主机本地内存与远程内存的所有优势。将这些与竞争节点放在一起,开发者可以控制复制、quorum以及共享或分离存储与处理的运行结果。下面是一些例子,体现了这种抽象化的力量:
交错启动允许应用程序在延迟时间和总系统工作时间之间进行权衡。
因为我们在讨论文件系统,所以元数据也应当成为我们讨论的话题之一。元数据通常由所有权、ACL、TTL等组成。OIL需要引用数据-DAG,用于描述如何、何时与何处进行I/O,因此通常也会在元数据中结束。元数据与分布式系统中的数据有许多相同的问题; 因此,“DAG抽象化可重用”是有一定意义的,这也是我们致力于实现的。
OIL允许定义两种DAG - 一个用于元数据,一个用于数据。这些DAG的执行框架和结构是相同的。唯一真正的区别是元数据DAG中的模块给出了一项key->原子值接口而非数据-DAG的key->字节流接口,并且元数据-DAG在数据-DAG之前执行。这种分离纯粹是为了方便,因为开发者可以在单个DAG中表达这一点。通常使用元数据-DAG来描述互斥、锁和其他序列化。描述如何以任意顺序读取和写入任意字节是在data-DAG中完成的。这些DAG显著不同之处在于数据DAG可以在文件的生命周期内发生变化,
自投入生产以来,OIL VCache已为实时视频流系统带来了显著的功能增强,并提高了可靠性,不仅降低了视频传输的延迟,还有效降低了存储和其他需求的计算开销。OIL在多个存储系统中的灵活性为开发人员提供了更丰富的可组合性选项,允许他们添加不超出硬件算力的功能。我们希望OIL VCache能够激发整个行业的抽象化与API的进一步创新。
我们要感谢所有参与OIL VCache项目的人。