Netflix发布了基于Java 的通用缓存项目Hollow 来缓存元数据。
在数据保存到集中式存储时,常常使用缓存技术去突破网络延迟、带宽等限制。在缓存数据的过程中,开发者经常要面对一个挑战,就是如何缓存频繁访问的本地数据和保存很少访问的远程数据。但如果能缓存所有需要访问的数据,那么实现我们的应用会更容易。Hollow 采用了将所有数据提供给每一个消费者的方法,而不是将数据分布到多台服务器上,不得不为数据的持久性而考虑用什么算法去复制。
Hollow 的前身是 Zeno ,它把数据作为普通 Java 对象(POJO)来保存,导致存储空间受到了限制。Hollow 则采用了一种紧凑、固定长度、强类型的数据编码方式避免了这个问题。
InfoQ 采访了 Netflix 的高级软件工程师和主要贡献者 Drew Koszewnik,向他请教了这个项目中的一些具体细节。
InfoQ:Hollow 和其它缓存产品,特别是 Memcached 相比,有什么不同?如果开发人员已经在使用别的产品,在什么情况下他们可能会转而关注这款全新的高速缓存产品?
Drew Koszewnik:Memcached 做得很好,在 Netflix 我们曾经用 EVCache 得到了很大收益,它可以适用于 Hollow 将要面对的缓存数据尺寸等问题。另一方面,Hollow 在别的一些问题上比 Memcached 解决得更有效,特别是在跨服务器编队中如何复制整个数据集。虽然在很多情况下,工程师们对这样做是否可行持怀疑态度,但其实会发现它的表现比预期更好。你可以认为 Hollow 是一种把你的数据集压缩到内存的方法,还能对它的任何部分提供 O(1)的访问速度。
Memcached 的主要好处是它是一个分布式缓存系统,通过很多分布式的实例来形成整个内存缓存池,但它仍然是一个中心化的系统。而我们认为 Hollow 不是一个真正意义上的分布式缓存系统,它其实是一个弥散性的系统。我们选用弥散性这个单词(分布式的反义词)来表示把整个数据集复制给每一个消费者。
Hollow 是为了满足 Netflix 对高吞吐量缓存的需求而创建的,大多数的设计决策最终都源自这个要求。对每一个消费者交互复制整个数据集意味着我们不必通过网络跳转来获取任何数据,所以访问数据集的任何部分所需的延迟是可以忽略不计的。对整个数据集范围内任何数据部分的访问所产生的 CPU 成本,Hollow 也非常关心。和 Memcached 那样松散类型的键 / 值存储不同,Hollow 采用强类型和结构化数据模型,这样可以定位消耗在数据访问上的成本问题,并在许多不同的场景中实现重用。我们选择去构建一个强类型的系统,先理解数据结构再建立各种工具。 History 和 diff 就是其中两个很好的工具,因为我们清楚数据模型,就能解释不同的两个状态之间究竟有什么变化,并围绕这些变化构建一些真正有用和通用性的应用界面元素。
顺理成章,有了完全本地结构化的数据后,在设计数据模型时你不必去考虑到底数据会被怎么使用。而在 Memcached 系统中,你不得不提前了解消费者会如何使用这些数据(它们只能利用预定义的键值来查询)。而在 Hollow 中,消费者团队可以自由地开发数据的访问模式,生产者团队也不会因此成为开发瓶颈。Hollow 在消费者端采用索引而不必在生产者端生成预定义索引,通过这种方式实现了访问模式的解耦。
InfoQ:它适用于只读数据、单生产者、多消费者的场景吗?您能详细介绍下数据是如何被持久化的吗?
Koszewnik:是的,它适用于只读数据、单个生产者、可能多个消费者的情形。Hollow 实现持久化的唯一机制是利用 BLOB 存储,它只是一个文件存储,可能是 S3、NFS、甚至一台 FTP 服务器。启动的时候,消费者们读取整个数据集的快照,并将数据集引导到内存中。通过增量的方法,可以保证内存中的数据集是最新的。对于每个消费者,内存中的数据备份是临时性的,如果消费者重新启动,需要从 BLOB 存储中重新加载快照。
实际上 BLOB 快照文件的格式很简单,它在很大程度上和在内存布局的结构相同,因此数据初始化主要是将 BLOB 的内容直接复制到内存中,这一步可以快速完成,确保了初始化的时间很短。
InfoQ:它能解决 CAP 定理中列出的问题么?例如,当出现间歇性的网络连接问题时,怎么处理过时数据?
Koszewnik:因为 Hollow 不是传统意义上的分布式存储,针对这个网络连接的问题对于 CAP 定理会有一些不同的考虑。我想你可以认为,Hollow 通过牺牲一致性来满足了可用性和分区容错性。
由于消费者都在 RAM 中保留了一份数据集的全拷贝,所以可用性是非常好的,一旦消费者完成初始化,完全不会受到由于环境不同而带来的问题所影响。至于分区容错性,它没有任何分区,对于每个消费者而言要么拥有整个数据,要么就没有。
一致性是比较有趣的话题,Hollow 将随时间而变化的数据集切割成离散的数据状态,每一个部分是特定时间点的数据快照。按照有规律的节奏,可以每隔几分钟生成这些快照。因此,必须要在一定程度上容忍数据集的传播延迟。你可以采取一些别的措施来缓解这个问题,例如,通过单独的渠道发送紧急更新来覆盖数据。
InfoQ:这种缓存机制并不是对所有的数据都有效,主要还是针对较小的元数据,对吗?如果本地服务器上的内存资源被耗尽会发生什么?
Koszewnik:对,Hollow 主要针对中小型数据集。根据经验,MB 到 GB 的数据规模比较合适,TB 或者 PB 就有点问题了。在因为一个数据集对于 Hollow 来说过大而被排除掉之前,不妨再考虑下,一个接近 1TB 的基于 JSON 的文档如果存在 Hollow 中会节省很多的空间。Hollow 的超高效存储性能结合一个优化后的数据模型可以把大的数据集转变成中等的。
正如 Google 的 Jeff Dean 所教导的,系统设计应该考虑数据增长性,确保它能在 10 倍或 20 倍数据增长情况下正常工作,暂时还不需要考虑 100 倍时的情况。这里 Hollow 的规则是,面对不断增长的数据集,提供一些工具对堆栈使用率进行细粒度分析,从而识别出要优化的目标。把分析出的指标按照时间做比较,这对于在早期发现潜在问题是非常有用的。特别要留意如果某个类型的数据出现了超线性的增长,也许就是数据模型设计得不够优化的信号,或者是新的增长维度被忽略了。
数据的建模在这里非常重要,数据结构化方法对 Hollow 删除重复数据的效果有着巨大影响,也决定了能压缩多少数据。一种通用的压缩算法是利用数据的特性来构造 Huffman 树,而 Hollow 是用数据模型里的记录结构作为数据特性。虽然这样做需要事先知道记录的结构,但关键是保持了随机访问的高吞吐量,同时实现了很好的压缩率。
InfoQ:请谈谈压缩算法,是什么让你们没有采用 POJO?
Koszewnik:Hollow 通过很多不同的方式实现压缩,其中包括去重、编码、打包、释放 Java 对象。虽然 Zeno 提供了去掉重复数据的方法,但如果不抛弃 POJO,我们也许就不能实现编码、打包、对象释放了。
再来说说性能,我们需要一种手段,以便在访问 Hollow 数据集时既能对 CPU 影响最小,又能让数据显示所需的堆占用最小。在数据布局时,我们在数据末尾用了一个固定长度的位对齐,这样每个字段占用所有记录最大值所需的字节数。然后,利用非对齐内存访问等技术来最小化检索数据所需的指令数。如果这样优化,Hollow 就可以支持 x86-64 框架,该框架目前要求用低字节序来排序,并且假定非对齐访问不会导致(或可以忽略)性能损失。在未来,会利用这些优化方案来实现更大的可移植性。
InfoQ:关于 Java 还有两个问题。为什么决定使用 Java 来实现(而不是 Go)?non-Java 的应用可以采用 Hollow 吗?
Koszewnik:我们用 Java 来实现 Hollow,因为我们每一个客户系统都用基于 JVM 的开发语言来建立它们的视频元数据,而基于 non-JVM 的语言目前还不能利用 Hollow。
InfoQ:最后一个问题。它是专门为 AWS 和 S3 特制的么?您能分享一些它的基准测试或者发展路线图吗?
Koszewnik:不,Hollow 没有提供或者特指什么架构,只是把产生的 BLOB 从生产者扩散到消费者。如果潜在的用户开始阅读快速启动指南,它介绍了如何嵌入基于AWS 的S3/DynamoDB 架构,也用一个项目示例介绍了如何实现可伸缩性的。不过相对容易点的,是把这些例子当成指南,学会在任何架构下实现_Publisher_、 Announcer、HollowBlobRetriever、HollowAnnouncementWatcher。
而对于不同的数据模型来说,它们各自的基准测试有很大的不同,所以我尽量避免下什么结论,因为用例的不同会误导出另一个方向的结论。可以下的结论,只是相比较 POJO 而言,Hollow 使用记录来访问数据,以极小的代价大大的减少了堆占用。
也没有严格的发展路线图,虽然有几件事我真的很希望能去解决,但从发展的角度看更希望能从我们的社区得到一些反馈。特别希望通过我们的努力,这个项目能让可用性得到极大的改进。
Hollow 的 github 网站提供了更多信息以帮助如何开始学习。
查看英文原文: Q&A with Drew Koszewnik on a Disseminated Cache,Netflix Hollow
感谢冬雨对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ , @丁晓昀),微信(微信号: InfoQChina )关注我们。
评论