本文是对于Snowflake论文的一个完整版解读,对于从事大数据数据仓库开发,数据湖开发的读者来说,这是一篇必须要详细了解和阅读的内容,通过全文你会发现整个数据湖设计的起初原因以及从各个维度(架构设计、存算分离、弹性伸缩、查询优化、故障恢复、性能优化等等)展开而来的设计思路。

可能内容本身站在当下并不显得新颖和具备创意,但是从2012年那个时候,云计算和大数据也才刚刚普及不久,在以结构化数据分析为主的行业里,出现了以非结构化和非模式数据的概念,并且是基于云计算为底座来做的。不得不说很有一定的挑战性。

下面内容是我对于各个不同段落加了一些自己的认知和解读,也是基于大数据经验和云计算从业者的一些认知进行了补充和完善。希望对于每个读者都能有不同程度的收获。

我们生活在分布式计算的黄金时代。公有云平台现在可以根据需要提供几乎无限的计算和存储资源。与此同时,软件即服务 (SaaS) 模型为那些由于成本和复杂性而无法负担此类系统的用户带来了企业级系统。遗憾的是,传统的数据仓库正在努力适应这种新环境。一方面,它们是为固定资源设计的,因此无法利用云的弹性。另一方面,它们对复杂的提取、转换和加载管道以及物理调优的依赖与云的新类型半结构化数据和快速发展的工作负载的灵活性和新鲜度要求相矛盾。

我们决定进行一次彻底的重新设计,我们的任务是为云计算构建一个企业级的数据仓库解决方案。 结果便是“ snowflake 虚拟数据仓库”, 简称 snowflake。snowflake 是一个多租户、 事务性的、 安全的、 高可扩展性和弹性的系统, 全面支持 SQL 和半结构化和非结构化数据的内建扩展。 这个系统以亚马逊云服务的形式提供, 按使用付费。 用户可以将他们的数据上传到云端,并且能够利用常见的 工具和接口立刻管理及查询数据。 项目于 2012 年底启动, snowflake 从 2015 年 6 月开始成为通用可用版本。 如今, snowflake 被越来越多的大小组织用于生产环境中。 这个系统每天处理数百万条查询并管理着多个 PB 的数据。

解读:

开头就重点输出了两点,并且整个论文开篇重点和SaaS化的数据仓库技术也是基于这两点问题展开的。

在当下云计算的时代,大数据还是以固有的资源体系架构进行设计,无法满足灵活的弹性伸缩能力。 在整个计算处理过程中,对于半结构化和非结构化数据的处理不够友好。

所以,snowflake的目标是解决这种在云资源之上实现极致弹性伸缩,能够全面支持SQL处理的半结构化和非结构化数据的能力,从snowflake的设计概念上体现为:是一个多租户、 事务性的、 安全的、 高可扩展性和弹性的系统

在本文中,我们描述了 Snowflake 的设计以及其新颖的多集群、共享数据架构。文章强调了 Snowflake 的一些关键特性:极端弹性和可用性、半结构化且无模式的数据、时间旅行和端到端安全。最后以经验教训和正在进行的工作展望作为结尾。

一、简介

云计算的出现标志着从在本地服务器上交付和执行软件,转向共享数据中心和基于云的软件即服务 (SaaS) 解决方案。 云共享基础架构承诺实现规模经济、极端可扩展性和可用性,并采用按使用量付费的成本模型来适应不可预测的使用需求。 但是,只有当软件本身能够弹性地扩展到云资源池时,才能利用这些优势。 传统数据仓库解决方案早于云计算。 它们旨在运行在小型静态群集上,这使它们成为较差的体系结构选择。

解读:

从大数据技术和云计算技术的诞生来说,都是解决的当下那个时代所面临的问题,大数据是在2008年前后发展起来,解决的是互联网时代数据量爆发引申出的大数据量数据分析和处理的问题,云计算的本质还是在于硬件发展速度受限,摩尔定律失效如何提升整个资源利用率的问题。在大数据诞生的时期,计算机硬件的发展还是非常迅速的。

但不仅平台变了。数据也变了。过去,数据仓库中的大部分数据都来自组织内部:事务系统、企业资源计划(ERP)应用、客户关系管理(CRM)应用等。结构、容量和速度都是可预测且已知的。但是云计算使得相当大且快速增长的数据量来自于不太可控或外部来源:应用程序日志、网络应用、移动设备、社交媒体、传感器数据(物联网)。除了不断增长的数量之外,这些数据经常以无模式的、半结构化格式出现。传统数据仓库解决方案正在努力解决这种新数据。这些解决方案依赖于深入的ETL管道和物理调优,从根本上假定来自主要内部来源的可预测、缓慢变化且易于分类的数据。

针对这些不足,数据仓库社区的一部分转向了“大数据”平台,如Hadoop或Spark。  虽然这些对于数据中心规模的数据处理任务来说是不可或缺的工具,而且开源社区仍在不断取得重大进展,例如Stinger倡议, 但它们仍然缺乏成熟的数据库技术所具有的许多效率和功能集。但是最重要的是,它们需要大量的工程努力才能推出和使用。

我们相信,有一大批用例和工作负载可以从云计算的经济性、弹性和服务方面受益,但这些用例和工作负载并不适合传统的数据仓库技术。

解读:

这一段讲了大数据的产生的背景,从过去单体架构服务所产生的事务型数据,到后面大数据量的产生而出现的大数据相关技术比如Hadoop和Spark,主要是通过分布式架构的技术来做这种多节点资源的分配和调度,但是从弹性和应用上来讲不是当时的核心问题。

通过大数据平台。所以我们决定为云环境构建一个全新的数据仓库系统。该系统被称为 Snowflake弹性数据仓库,或“Snowflake”。与许多其他基于云计算的数据管理系统的不同之处在于,Snowflake 并不是建立在Hadoop、PostgreSQL 或其他类似技术之上。其处理引擎和其他大部分组件都是从头开始开发的。Snowflake 的关键特性如下:

纯软件即服务(SaaS)体验 用户无需购买机器、聘请数据库管理员或安装软件。用户的数据要么已经在云端,要么他们上传(或邮寄[14])。然后,他们可以立即使用Snowflake的图形界面或标准化接口(如ODBC)来操作和查询数据。与其他基于云的关系型数据库服务(DBaas)不同,Snowflake的服务范围涵盖整个用户体验。用户没有调整旋钮,没有物理设计,也没有存储整理任务。 关系型 Snowflake 具有对 ANSI SQL 和 ACID 事务的全面支持。大多数用户能够通过很少或没有更改来迁移现有的工作负载。 半结构化Snowflake提供内置函数和SQL扩展,用于遍历、扁平化和嵌套半结构化数据,并支持流行的格式,如JSON和Avro。自动元数据发现和列存储使对无模式、半结构化数据的操作几乎与普通关系型数据一样快,无需用户付出任何努力。 存算分离、平滑地进行扩展,不会影响数据可用性或并发查询性能。 高可用,Snowflake可以容忍节点、集群甚至整个数据中心的故障。在软件或硬件升级期间不会发生停机。 持久化,为防止意外数据丢失而设计的,具有额外的安全保护措施:克隆、撤销删除以及跨区域备份。 低成本 Snowflake 非常高效且计算资源丰富,所有表数据都经过压缩。 用户只需为其实际使用的存储和计算资源付费。 数据可靠性,包括临时文件和网络流量都得到了端到端加密。没有用户数据会被暴露给云平台。此外,基于角色的访问控制使用户能够在 SQL 层级上对访问进行细粒度的控制。

解读:

这一段可以说是snowflake整个设计思想的核心点,这里从云数据仓库提供服务的模式、对于标准SQL兼容性、存算分离架构、基于云所带来的容错能力(跨可用区)、数据安全措施、成本效益这几个方面考虑设计,其实从技术角度来看都没有问题,唯一的在于成本效益这块,在这个段落里是说表被压缩,并且存储和计算资源付费,但是在后面的内容中,整个成本计算方式是按照处理性能的登记来划分的。

Snowflake 目前运行在亚马逊云(Amazon Web Services,AWS)上,但未来我们可能会将其移植到其他云计算平台上。截至本文撰写时,Snowflake 每天处理数百万条查询,并为来自不同领域的众多大型和小型组织提供服务。

本文的结构如下。

第 2 节解释了 Snowflake 的关键设计选择:存储与计算分离。

第 3 节介绍了由此产生的多集群共享数据架构。

第 4 节突出了不同的特点:连续可用性、半结构化和无模式数据、时间旅行和克隆,以及端到端的安全性。

第 5 节讨论了相关工作。

第 6 节总结了本文的工作成果并展望了正在进行中的研究。

二、存储与计算

Shared-nothing 架构已经成为高性能数据仓库系统的主要体系结构,主要原因有两个:可扩展性和商用硬件。在Shared-nothing 架构中,每个查询处理节点都有自己的本地磁盘。表是跨节点水平分区的,每个节点只负责本地磁盘上的数据行。这种设计非常适合星型模式查询,因为只需要很少的带宽就可以将小(广播)维度表与大(分区)事实表连接起来。由于对共享数据结构或硬件资源的竞争很小,因此不需要昂贵的定制硬件

在Shared-nothing 架构中,每个节点都有相同的职责,并且运行在相同的硬件上。这种方法导致了优雅的软件,易于理解。然而,Shared-nothing架构有一个重要的缺点:它紧密地耦合计算资源和存储资源,在某些情况下会导致问题。

解读:

Shared-nothing架构是一种分布式系统中常用的架构模式,它将每个节点上的资源都视为独立的,不会共享任何资源。这种架构的优点是可以提高系统的可扩展性和容错性,但是也会增加通信成本和数据复制的工作量。

Hadoop等大数据相关服务通常是基于Shared-nothing架构设计的。这种架构中,每个节点都有自己的本地存储器和计算资源,不同的节点之间不会共享数据和硬件资源。这样可以提高系统的可扩展性和容错性,并且能够更好地适应大规模分布式环境下的数据处理需求。

而Shared-everything架构则是将所有节点上的资源都共享在一起,包括硬件资源和软件资源。这种架构的优点是可以减少通信成本和数据复制的工作量,但是会降低系统的可扩展性和容错性。

异构工作负载 硬件配置是一致的,但工作负载通常不是。 一个对批量加载(高I/O带宽、轻计算)最理想的系统配置 对于复杂的查询(低IO带宽、重计算)来说却并不适用,反之亦然。 因此,硬件配置需要以较低的平均利用率作为代价。

成员变更 如果节点集合发生变化,无论是由于节点故障还是用户选择调整系统大小,都需要重新洗牌大量数据。 由于相同的节点负责数据洗牌和查询处理,因此可以观察到显着的性能影响,限制了可扩展性和可用性。

在线升级 尽管使用复制可以降低小规模成员更改的影响,但软件和硬件升级最终会影响系统中的每个节点。在理论上,一个接一个地升级节点而不让系统中断是可能的,但由于所有东西都紧密结合且期望一致,这变得非常困难。

在本地环境,这些问题通常是可以容忍的。工作量可能异构,但当只有一个很小的、固定的节点池可以运行时,人们几乎无能为力。很少对节点进行升级,也很少发生节点故障或系统调整大小的情况。

云计算的情况则完全不同。像亚马逊EC2这样的平台有很多不同类型的节点[4]。利用它们只需要把数据带到正确的节点类型即可。与此同时,节点故障更加频繁,性能差异也会很大,即使是在相同类型的节点之间也是如此[45]。因此,成员资格的变化并不是例外,而是常态。最后,实现在线升级和弹性扩展有很强的动力。在线升级可以大大缩短软件开发周期,提高可用性。 弹性伸缩进一步提高了可用性,并允许用户匹配其当前需求与资源消耗。

出于这些原因和其他原因,Snowflake 将存储与计算分离。这两个方面由两个松散耦合、独立可扩展的服务处理。计算通过 Snowflake 的(专有)共享无关引擎提供。存储通过 Amazon S3 提供,但原则上任何类型的 blob 存储库都足够了。为了减少计算节点和存储节点之间的网络流量,每个计算节点都在本地磁盘上缓存一些表数据。

这种解决方案的另一个好处是,本地磁盘空间没有用于复制整个基础数据集,这可能非常大并且大多是冷(很少访问)。相反,本地磁盘专门用于临时数据和缓存,两者都是热的(建议使用SSD等高性能存储设备)。因此,一旦缓存变暖,性能就会接近甚至超过纯共享无关系统。我们称这种新颖的体系结构为多集群、共享数据架构。

解读:

在大数据技术中,存储和计算是耦合在一起的,并且在节点扩容时要求硬件配置相同(管控平台可以设置节点组),那这时候就会造成要么存储资源浪费、要么计算资源浪费,使得服务器资源使用不均衡。其次,在数据节点缩容的时候,数据会进行副本之间的同步和迁移,对于整个系统稳定性来说是有一定风险的。

对于云上集群来说,当节点故障的时候,大数据场景下也会出发数据迁移和恢复,无论是节点故障、磁盘故障还是网络故障,所以,对于大数据场景存算一体下的极致的弹性伸缩能力,是基本无法做到的。

所以,Snowflake通过将存储和计算分离这种松耦合的架构,来实现更高效率的弹性和资源利用问题。

三、架构设计

Snowflake 是为大型企业而设计的服务。除了提供高可用性和互操作性外,企业就绪意味着高可用性。为此,Snowflake 是一个由高度容错且可独立扩展的服务组成的面向服务的架构。这些服务通过 RESTful 接口进行通信,并分为三个架构层:

解读:

Snowflake 是为大型企业而设计的服务,从定位上就是为大企业而服务,小企业或者小数据量的公司可能都不足以开通资源的费用呢。

数据存储层 使用 Amazon S3 存储表数据和查询结果。

虚拟仓库 系统的“肌肉”。这一层负责在被称为虚拟仓库的弹性虚拟机集群中执行查询。

云服务 系统的大脑。这一层是一组服务,用于管理虚拟仓库、查询、交易以及与此相关的所有元数据:数据库模式、访问控制信息、加密密钥、使用统计数据等。

解读:

从整个架构上分为三层架构,分别为数据存储、数据仓库、云服务,存储层使用对象存储S3来存储数据,在数据仓库层主要做查询解析、资源管控、集群管理等等,云服务主要用来做一些元数据相关的维护。

图 1 描述了snowflake架构的三个分层以及它们的主要组件。

3.1数据存储

选择 Amazon Web Services (AWS) 作为 Snowflake 的初始平台,原因有二。首先,AWS 是云平台市场上最成熟的产品。其次(也是与第一点相关),AWS 提供了最大的潜在用户群。

接下来的选择是在使用S3还是基于HDFS或类似的东西开发自己的存储服务之间进行选择。 我们花了一些时间来尝试S3,发现尽管它的性能可能会有所不同,但其可用性、高可用性和强大的持久性保证很难被打破。因此,我们决定不再开发自己的存储服务,而是将精力投入到虚拟仓库层的本地缓存和倾斜恢复技术中。

解读:

从当下时期来说,貌似使用对象存储来做这种存算分离架构的技术基本成熟,但是回望2012年那时候基于HDFS分布式文件存储技术是如日中天,对象存储想对还没有那么领先,而且从性能、操作上来说不如文件系统,所以说,这个选择应该也是经历多了长时间的讨论和抉择的,毕竟基于对象存储的选型来说,对于K/V这种存储类型,是不适用于大数据场景的,事实也证明在大数据计算场景下的LIST和RENAME是有极大的瓶颈问题的。所以说,如何做和计算和存储之间的数据存储层是整个存算分离技术的核心关键点之一。

与本地存储相比,S3 的访问延迟自然要高得多,每个 I/O 请求都会产生更高的 CPU 负担,尤其是如果使用了 HTTPS 连接。但更重要的是,S3 是一个 blob 存储库,一个相对简单的基于HTTP(S) 的PUT/GET/DELETE 接口。对象即文件,只能被完全(重新)写入。甚至不能向文件末尾追加数据。实际上,在PUT 请求中必须提前声明文件的确切大小。但是,S3 支持针对文件的部分(ranger)的GET 请求。

这些特性对 Snowflake 的表文件格式和并发控制方案产生了很大影响(参见第 3.3.2 节)。表被水平拆分为大而不可变的文件,相当于传统数据库系统中的块或页。在每个文件中,每个属性或列的值都被分组并进行高度压缩,这是文献[2]中称为 Pax 或混合列存储的一种众所周知的方法。每个表文件都有一个标题,除了其他元数据外,它还包含文件中每列的偏移量。由于 S3 允许对文件的部分进行 GET 请求,查询只需要下载文件头以及它们需要的那些列。

Snowflake 不仅使用 S3 存储表数据,还使用 S3 存储查询运算符(例如大规模连接)生成的临时数据。当本地磁盘空间不足时,它会将临时数据写入 S3 以存储大量查询结果。 将临时数据写入 S3 可让系统计算任意大小的查询而不会出现内存或磁盘不足错误。 将查询结果存储在 S3 中可以启用新的客户端交互形式并简化查询处理,因为它消除了传统数据库系统中需要的服务端游标。

元数据,如目录项、包含哪些S3文件的表、统计信息、锁、事务日志等都存储在可扩展的事务性键值存储中,它是云服务层的一部分。

3.2虚拟仓库

虚拟仓库层由 EC2 实例组成。每个这样的集群都通过一个称为虚拟仓库 (VW) 的抽象呈现给单个用户。构成 VW 的单个 EC2 实例称为工作节点。 用户永远不会直接与工作节点交互。实际上,用户不知道也不关心哪些工作节点构成了 VW 或者有多少个工作节点。取而代之的是,VW 是以抽象的“T 恤尺码”形式提供的,从 X 小到 XX 大不等。这种抽象使我们能够独立于底层云平台来发展服务和定价。

解读:

虚拟数据仓库可以理解为是一种逻辑上的数据仓库集合,在当年2012年的时候,容器技术还没有发展起来,所以当时设计还是以虚拟机节点来做为计算层,这里还有一个关键的两点,其实在上面也简单提到了就是成本效益问题。

使用者(用户)是不知道底层到底有多少个VM节点的,用户只是感知到数据库连接地址,然后进行读写数据即可。 关于计费计量方面,一般Pass平台是按照CPU/Memory,有的按照CU来计量单位,1CU大概表示1C4G的空间,但是在这里是通过更加抽象的逻辑单位来表示的,比如X、XL、XXL、XXXL等等,不同的XX能够处理的数据量、计算量也不同,通常来说是逐步扩大的。

最后一句话,也提到了,这种定价模式是方便于云数据仓库这种SaaS产品独立于云平台来发展和定价。一般云厂商定价的规则是包年包月、按量付费的逻辑,计量的单位是以节点规格来收费的,另外加上一些服务费用,相对更加透明一些。

但是Snowflake的收费模式,就没有那么透明,很难解释清楚定价逻辑和规则,所以从成本效益上可能会有减少,也可能会有增多,这要具体看使用者的业务处理方式。

3.2.1弹性和隔离

虚拟机(VM)是一种纯粹的计算资源。它们可以根据需要在任何时间创建、销毁或调整大小。创建或销毁 VM 不会影响数据库的状态。用户在没有查询时关闭所有 VM 是完全合法的(并且受到鼓励)。这种灵活性使用户能够根据使用需求动态匹配计算资源,而无需考虑数据量。

解读:

这段其实是很有料的,不知道读者朋友是否对于云计算有所了解,一般云上主机(虚拟机)是有弹性策略的,但是对于数据仓库、大数据这种场景,主机资源通常是7*24小时开机状态,因为从付费模式上就决定了关机或者不关机没有成本上的缩减。

所以,除非是走退订的逻辑,将主机进行释放,当需要的时候在重新购买然后开机部署使用,这种模式需要使用者具备一定的技术能力和门槛,国内云厂商(注意是国内)通常并没有针对大数据场景做这种标准化的处理模式。而且这种模式对与IASS的弹出性能有一定的要求,不过幸运的一点在于,当下基于Kubernetes的容器化技术的弹性伸缩性能要更快一些,如果使用一些高性能的容器管控服务的话,秒级别可以弹出几十甚至几百个Pod都是可能的,当然这里要依赖很多容器化内核的技术,比如镜像预拉取(缓存)、调度服务、容器运行时、配置管理等等。

每个单独的查询只运行在一个VW上。工作节点不会在多个VW之间共享,从而为查询提供了强大的性能隔离。(话虽如此,我们认为工作节点共享是未来工作的一个重要领域,因为它将为性能隔离不是大问题的用例提供更高的利用率和更低的成本。)

解读:

我理解这里的隔离技术指的就是当下Kubernetes的worker和Pod之间的关系,显然,当下的容器化技术就是提升了资源利用率,并且,提供了强大的资源隔离策略,成本也相比虚拟机有了一定程度的降低。

注意:这篇论文是2012年提出的,容器化技术大概是在2014年才开始。

当提交一个新查询时,每个 VW 工作节点(如果优化器检测到小查询,则为子集)都会产生一个新的工作进程。 每个工作进程只存在一个查询的时间长度内。 即使作为更新语句的一部分,单独的工作进程也永远不会对外部可见的影响,由于表文件是不可变的,参见第3.3.2节。因此,工作节点的故障很容易被控制,并通过重试来解决。Snowflake目前不支持部分重试,因此非常大的、长时间运行的查询是一个需要关注和未来工作的重点。

解读:

分布式计算引擎大多是MPP(并行计算架构),对于单个Task的失败、重试会直接影响整个作业的最终结果成功失败、运行效率的核心因素,先从两个方面来说吧。

不可变文件天然优势,如果使用Scala写Spark作业的话,会发现里面很多算子和函数都是不可变的,不可变的好处就在于不用担心数据的变化,而重新刷新所有已经计算完成的数据集,所以重试、重跑的效率更高一些。 大任务大数据量很容易出现数据热点问题,这是所有MPP架构都会遇到的问题,例如Shuffle数据、数据倾斜都会导致这种问题的出现,所以各种计算引擎都在做数据倾斜方面的优化,但是倾斜并不是一个技术上能够完全避免的问题,从使用角度(业务)上来说也需要一定的设计和优化。

每个用户可以同时运行多个虚拟机,而每个虚拟机又可以并发执行多个查询。每台虚拟机都可以访问相同的共享表,无需实际复制数据。

共享、无限存储意味着用户可以分享和整合他们所有的数据,这是数据仓库的核心原则之一。同时,用户还可以从私有的计算资源中受益,避免不同工作负载和组织单位之间的干扰——这也是数据集市的原因之一。这种弹性和隔离使得一些新的用例策略成为可能。Snowflake用户通常会为来自不同组织单位的查询拥有多个虚拟仓库(VW),这些查询通常会持续运行,并会定期启动按需VW,例如用于批量加载。

解读:

先说从模式上来讲,上面提到对于使用者来说就是购买了XXL的资源,对于存储来说是基于对象存储来存储的,对象存储的好处就是不需要关心实际存储用量大小,可以说一种无限大的存储资源池,想要写多少数据都可以,并且支持不同等级的冷热分层策略。

其次,是说计算资源隔离问题,我理解当时的隔离技术可能并不是很成熟,更多的可能是未来的一种对于计算资源隔离的“设想”,但是从目前的实际来看,这种是能够实现的, 资源隔离首先要有强大的资源调度引擎,可以进行内核级别的资源管控,同时对于碎片化的资源(例如:内存)能够充分的利用上。

与弹性相关的另一个重要观察是,通常可以以大致相同的价格实现更好的性能。例如,在一个具有四个节点的系统上需要 15 小时的数据加载任务可能只需要在拥有32个节点的系统上花费两小时 , 由于每小时计算成本,总体成本非常相似——然而用户体验却大不相同。因此我们相信 VW 的伸缩性是 Snow-Flake 架构的最大优势之一,并且它表明需要一种新的设计来利用云的独特功能。

解读:

这其实就是多角度看待问题的一种方式,是选择多节点快速跑完还是选择少节点多时间来跑完,并没有好坏之分,这里突出的重点在于Snowflake的极致弹性伸缩能力,就是说你可以选择快速把资源打满,然后快速跑完任务。这是产品本身能够达到的效果,换句话说给了用户更多选择的空间,体验度上更好一些。毕竟一些紧急的作业是非常需要这种特性支持的。

3.2.2本地缓存与文件窃取

解读:

这一节重点解决的应该就是数据存储处理问题,上面有提到对象存储是有大数据量计算瓶颈问题的, 所以在大数据场景下,必然会有很强大的缓存能力才能够做到既高效又经济的作业执行。

每个工作节点都维护本地磁盘上的表数据缓存。缓存是一组表文件,即过去由该节点访问过的 S3 对象。具体来说,缓存包含文件头和文件中的每一列,因为查询只会下载所需的列。

解读:

对象存储的优化方案就是尽量减少大批量的访问,这样对于访问过的信息(元数据)可以记录在本地缓存中,这样当下次再进行访问时,就可以通过本地缓存的元数据信息(比如Key的位置),直接到GET请求到对应的KEY信息。就不用单独的LIST的方式挨个扫描一遍了,这是对于S3等对象的其中一种处理方式。

当下来说,很多开源的系统也支持直接链接对象存储元数据缓存和数据缓存,例如:JuiceFS、Alluxio都可以支持挂载/链接对象存储来实现高效的数据读写。

缓存在工作节点期间存在,并在并发和后续工作进程(即查询)之间共享。它只看到一个文件和列请求流,并遵循一个简单的最近最少使用(least-recently-used, LRU)替换策略,忽略单个查询。这个简单的方案工作得非常好,但我们可能会在将来对其进行改进,以更好地匹配不同的工作负载。

为了提高命中率并避免在VW的工作节点上对单个表文件进行冗余缓存,查询优化器使用表文件名上的一致散列将输入文件集分配给工作节点。因此,访问同一表文件的后续或并发查询将在同一工作节点上执行此操作。

解读:

在大多数的存算分离架构下的系统来说,基本上都会有一致性Hash算法的应用(例如:Apache Pulsar的Bundle概念),这种算法本质上来说就是提供了存算分离场景下的任务均衡调度能力,能够充分的将任务进行打散,并且根据节点数量的大小可以平滑的进行Hash重调度。

一致散列算法是一种用于分布式系统中的负载均衡算法,数据集映射到一个环上,每个节点被分配到环上的某个位置,然后根据数据集中的元素所对应的环上的位置来确定哪个节点负责处理该元素。这种算法的优点是可以实现平滑的扩容和缩容,因为只需要添加或删除少量节点即可重新平衡负载,而不需要重新分配整个数据集。此外,一致散列还可以减少网络流量和数据复制的数量,提高系统的可靠性和可用性。

Snowflake中的一致性哈希是惰性的。当工作节点集合发生变化时(由于节点故障或VW调整),不会立即进行数据洗牌。相反,snowflake依赖LRU缓存淘汰策略(最近最少使用)策略来最终重新放置缓存内容。这种解决方案将替换缓存内容的成本分摊到多个查询中,从而比急切的缓存或纯shared-nothing系统具有更好的可用性,后者需要立即在节点之间shuffle大量表数据。它还简化了系统,因为没有“降级”模式。

解读:

通常来说缓存不是无限大的,能够提供缓存的大小是有限的,所以基于本地缓存的策略一般是通过时间周期(TTL)或者LRU算法(最近最少使用)来实现。

除了缓存之外,数据倾斜在云数据仓库中尤为重要。由于虚拟化问题或网络争用,某些节点的执行速度可能比其他节点慢得多。在其他地方,Snowflake在扫描级别处理这个问题。每当一个工作进程扫描完它的输入文件集,它就会从它的对等进程请求额外的文件,我们称之为文件窃取技术。如果对等节点发现当这样的请求到达时,它的输入文件集中还有许多文件,它通过在当前查询的持续时间和范围内转移剩余一个文件的所有权来应答请求。然后请求者直接从S3下载文件,而不是从对等节点。这种设计确保了文件窃取不会因为在离散节点上增加额外的负载而使事情变得更糟。

解读:

这里说到的数据倾斜就是在上面提到MPP架构里面的痛点,本段落里给出的可能是因为节点故障或者网络通信问题,其实还有很多很多因素,那在对于数据倾斜问题上,snowflake的处理方式是:

A和B两个进程处理的可能是同一个Stage的数据集,A先处理完成了, 但是B卡顿了一只没有处理完成,那么A会通过RPC请求到B中,B会把一部分数据的信息给到A,然后A根据这些元数据信息拉取相关数据进行处理,那么B就不需要处理这部分数据里了。

3.2.3执行引擎

如果另一个系统可以使用10个这样的节点在相同的时间内执行查询,那么能够对超过1000个节点进行查询几乎没有价值。因此,虽然可扩展性很重要,但每个节点的效率同样重要。我们希望为用户提供市场上任何数据库即服务(Database-as-a-Service)产品中性能/价格比最佳的产品,所以我们决定实现自己的最先进的SQL执行引擎。我们构建的引擎是基于列、向量化和push-based。

解读:

基于向量化、列式存储的计算引擎是当下很多大数据存储/分析系统近几年才开始做的,比如Doris、Starrocks、Spark、CK等向量化能力也是近几年开始实现的,但是Snowflake在十几年前就开始通过向量化、列存储来构建自己的计算引擎了。

列存储和执行通常被认为比行存储和执行更适用于分析工作负载,因为它更有效地利用了CPU缓存和SIMD指令,并提供了更多轻量级压缩的能力

向量化执行意味着,与 MapReduce 等方法相比,Snowflake 避免了中间结果的物化。相反,数据以pipeline方式处理,在列存储格式中按数千行一组进行批量处理。这种由 Vectorwise(最初为 MonetDB/X100[15])开创的方法节省了 I/O 并极大地提高了缓存效率。

基于Push-based的执行是指关系运算符将它们的结果推送给下游运算符,而不是等待这些运算符拉取数据(经典的火山模型)。 基于推送的执行提高了缓存效率,因为它从紧密循环中删除了控制流逻辑。它还使Snowflake能够有效地处理DAG形状的计划,而不仅仅是树形结构,从而为共享和pipeline中间结果创造了额外的机会。

解读:

基于Push-based的执行逻辑,在很多计算引擎运算中都有用到,如果对于spark shuffle处理机制比较熟悉的话,对push-based会更容易理解。在Spark shuffle中通过将数据集划分为多个分区并对每个分区进行排序,然后在不同的节点之间交换这些分区的数据,最后对所有分区的数据进行合并,以生成最终的结果。具体来说,就是当一个TaskA需要访问另一个TaskB的输出时,该TaskA会向另一个TaskB发送一个请求,并且TaskB会在接收到请求后立即将其输出发送回请求方,而不是等到下一个轮询周期才发送。这样做的好处是可以减少网络通信的开销并提高任务之间的协作效率。

同时,传统查询处理中的许多开销在Snowflake中不存在的。特别是,在执行过程中不需要事务管理。就引擎而言,查询是针对一组固定的不可变文件执行的。此外,没有缓冲池。大多数查询会扫描大量数据。将内存用于表缓冲而不是操作是一个不好的权衡。然而,Snowflake允许所有主要操作符(join、group by、sort)溢出到磁盘并在主内存耗尽时递归。我们发现,纯主内存引擎虽然更精简、更快,但过于严格,无法处理所有有趣的工作负载。分析工作负载可能具有非常大的连接或聚合。

解读:

简单可以理解为中间shuffle数据结果落盘,内存的容量比磁盘要少很多,所以大多数的计算引擎在计算时会将shuffle数据进行落盘处理,这个在当下大多数分布式计算引擎中都有相关实现。

3.3云服务

虚拟仓库是短暂的、特定于用户的资源。相比之下,云服务层是高度多租户的。该层的每个服务——访问控制、查询优化器、事务管理器等——都是长期存在的,并且在许多用户之间共享。多租户改善了利用率,减少了管理开销,比传统架构中每个用户都拥有完全私有系统实例的情况下实现更好的规模经济。

解读:

对于云数据仓库而言,是立于云的基础设施之上的,对于图一的架构图而言,中间的VW数据仓库其实是一种无状态的计算资源,是和用于进行绑定的,但是在这些资源之上,对于租户管理、查询器优化、事务管理是snowflake平台化的一种能力。

每个服务都有副本以实现高可用性和可扩展性。因此,单个服务节点的故障不会导致数据丢失或可用性的丧失,一些正在运行的查询可能会失败(并被重新执行)。

3.3.1查询管理与优化

用户发出的所有查询都通过云服务层。在这里,处理所有查询生命周期的早期阶段:解析、对象识别、访问控制和计划优化。

解读:

这里的云服务层更多的是部署在云上的服务,这些服务主要作用就是做查询解析的工作,生成查询计划以及查询计划优化等等。

Snowflake 的查询优化器遵循典型的 Cascade (级联式)风格方法[28],采用自顶向下的基于成本的优化。所有用于优化的统计信息都会在数据加载和更新时自动维护。由于 Snowflake 不使用索引(参见第 3.3.3 节),因此计划搜索空间比某些其他系统要小。通过将许多决策推迟到执行时间,例如连接的数据分布类型,可以进一步缩小计划空间。这种设计减少了优化器做出的糟糕决策数量,在以微小的性能损失为代价的同时提高了鲁棒性。它还使系统更易于使用(性能变得更加可预测),这符合Snowflake对服务体验的整体关注度。

解读:

这个是对于内部查询引擎所使用的技术相关。当作了解即可,毕竟我们也不是做查询器的。

自顶向下的搜索过程中,整个搜索空间会形成一个Operator Tree, Cascades首先将整个Operator Tree按节点拷贝到一个Memo的数据结构中,每个Operator放在一个Group。对于有子节点的Operator来说,将原本对Operator的直接引用,变成对Group的引用。

一旦优化器完成,生成的执行计划就会分发给查询中参与的所有工作节点。随着查询的执行,云服务会持续跟踪查询的状态以收集性能计数器并检测节点故障。所有查询信息和统计都用于审计和性能分析。用户可以通过Snowflake图形用户界面监视和分析过去的和正在进行的查询。

3.3.2并发控制

如前所述,Snowflake 的并发控制完全由云服务层处理。Snowflake 是为分析工作负载而设计的,这些工作负载往往是由大量读取、批量插入或点滴插入,以及批量更新。与该工作负载空间中的大多数系统一样,我们决定通过快照隔离实现ACID 事务。

在SI下,事务读取的所有内容都看到了一个一致的数据库快照,该快照是事务开始时的状态。通常情况下,SI是建立在多版本并发控制(MVCC)之上的,这意味着每个更改的数据库对象的副本都会被保留一段时间。

解读:

这里的SI指的是(Snapshot Isolation),这里的快照隔离技术是数据库里面用于实现ACID事务的基本原则,简单来讲,就是通过在每个事务开始的时候创建一个视图,这个视图包含了当前事务开始之间所有已经提交事务的信息,并且在此之后所有未提交的事务不会影响当前的视图,就避免了脏读(Dirty Read)、不可重复读(Non-Repeatable Read)和幻读(Phantom Read)等问题,同时也满足了事务的隔离性和持久性要求,当事务结束并提交时,所有对数据的修改都会被永久保存下来,保证了数据的一致性和可靠性

由于表文件是不可变的,因此使用S3进行存储的直接结果是MVCC是一种自然的选择。文件的更改只能通过用包含更改的不同文件替换来完成。这意味着对表执行写操作(插入、更新、删除、合并)会产生一个新版本的表,通过相对于先前的表版本添加或删除整个文件来实现。文件的添加和删除在元数据中进行跟踪(在全局键值存储中),以一种允许计算属于特定表版本的文件集的方式。

解读:

S3做为对象存储是不支持文件变更的,所以会发现在很多数据湖组件中(iceberg、hudi)都有版本变更的概念,每次在修改一个文件操作时,其实是基于一种Copy On Write的思想来重新生成了一个新的文件,然后替换掉了之前文件。

这是数据湖组件选择使用对象存储作为数据存储方式所必然要做的选择,也就是MVCC(多版本控制)

除了SI之外,Snowflake还使用这些快照来实现时间旅行和数据库对象的高效克隆。有关详细信息,请参阅第4.4节。

解读:

时间旅行的概念如果现在有接触到数据湖技术的话,这个应该是比较清楚的,简单来讲,就是Snowflake使用快照技术可以在保留一定时间内的历史版本的同时,快速恢复到过去的某个时间点或者创建一个完整的备份副本(一种跨越时间窗口的数据恢复能力)。

3.3.3剪枝

解读:

剪枝的实现在OLAP架构中非常的常用,通俗来讲就是对查询条件进行过滤,避免不必要的计算和扫描,从而提高查询性能的一种技术。

其原理是利用多维数据的特点,在查询时根据当前维度和度量值的状态,预测出下一步需要计算的数据,并且只计算必要的部分,而不是全部计算。这样可以大大减少计算时间和内存消耗,提高查询速度和效率。具体实现方法包括前缀剪枝、后缀剪枝、中间状态剪枝等多种策略。

将访问权限限制在与给定查询相关的数据上是查询处理中最重要的方面之一。通常来讲,通过使用B+树或类似的数据结构来限制数据库中的数据访问。虽然这种方法对于事务处理非常有效,但对于像Snowflake这样的系统,它会引发多个问题。首先,它严重依赖于随机访问,这是由于存储介质(S3)和数据格式(压缩文件)的问题。其次,维护索引会显著增加数据量和数据加载时间。最后,用户需要明确地创建索引,这将与Snowflake的纯服务方法非常不符。即使在调优的帮助下,维护索引也可能是一个复杂、昂贵和风险高的过程。

最近,一种新的技术在大规模数据处理中变得流行:基于最小最大值的剪枝,也称为小量聚合、区域映射和跳读。在这种情况下,系统维护给定数据块(记录集、文件、块等)的数据分布信息,特别是该块中的最小值和最大值。根据查询谓词,这些值可以用来确定给定查询是否需要给定的数据块。例如,假设文件f1和f2包含列x的值分别为3..5和4..6。那么,如果查询有一个谓词WHERE x>= 6,则我们知道只需要访问f2。与传统索引不同,这种元数据通常比实际数据小几个数量级,导致存储开销小并且访问速度快。

剪枝很好地符合snowflake设计原则:它不依赖于用户输入;它能够扩展;并且易于维护。此外,对于大量数据的顺序访问,它的效果很好,并且在加载、查询优化和执行时间方面几乎没有开销。

Snowflake 会为每个实例保留与剪枝相关的元数据。单个表文件。元数据不仅包括普通的关联列,还包括半结构化数据中自动检测到的一些列,如第 4.3.2 节所述。在优化过程中,会使用查询谓词来检查元数据,以减少(或剪枝)输入文件集以执行查询。优化程序不仅针对简单的基值谓词进行剪枝,还针对更复杂的表达式进行剪枝,例如WEEKDAY(orderdate)IN(6、7)。

除了静态剪枝之外,Snowflake 还在执行过程中进行动态剪枝。例如,在作为哈希连接处理的一部分时,Snowflake 会收集构建侧记录中连接键分布的统计信息。然后,这些信息会被推送到探查侧,并用于过滤,以及可能跳过探查侧上的整个文件。除此之外,还有其他众所周知的技术,如 Bloom Join [40]。

四、特色功能和亮点

Snowflake 提供了许多预期从关系型数据仓库获得的功能:全面的 SQL 支持、ACID 事务、标准接口、稳定性与安全性、客户支持,以及、当然、强大的性能和可扩展性。此外,它还引入了一些其他功能,这些功能在相关系统中很少见到,或者从未见过。本节介绍一些我们认为是技术差异化的功能。

解读:

目前这些功能和特性基本上像hudi、iceberg的数据湖组件都支持的,而且基于SaaS化的商业化企业也有很多。

所以本节从当下来看,并没有什么特别的地方,主要就是Web的可视化访问,支持Ad-hoc功能,可视化的数据库表管理、用户权限控制(基于数据库的)、监控、查询用量等等。

4.1纯SaaS化体验

Snowflake 支持标准数据库接口(JDBC、ODBC、Python PEP-0249),并与诸如Tableau、Informatica或Looker等第三方工具和服务集成。然而,它还提供了仅使用网络浏览器与系统交互的可能性。web 界面似乎是一件小事,但它很快证明了它是关键差异因素。Web 界面使从任何位置和环境访问 Snowflake 非常容易,并大大减少了启动和使用系统的复杂性。由于大量数据已经存储在云中,它使得许多用户可以指向他们的数据并查询,而无需下载任何软件。

不出所料,UI 不仅支持 SQL 操作,还提供了数据库目录、用户和系统管理、监控、使用信息等。我们不断扩展 UI 功能,专注于在线协作、用户反馈和支持等方面的工作。

但我们在易用性和用户体验上的关注,并不局限于用户界面;它延伸到系统架构的方方面面。没有失败模式,也没有调优旋钮、物理设计或存储优化任务。这一切都是关于数据和查询。

4.2持续可用性

过去,数据仓库解决方案是深藏不露的后端系统,与世隔绝。在这样的环境中,计划内(软件升级或管理任务)和非计划内(故障)停机时间通常不会对运营产生重大影响。但随着数据分析越来越多地成为业务任务的关键部分,任何数据仓库都必须保持持续可用性。这一趋势反映了现代SaaS系统的期望,它们大多都是始终运行、面向客户的应用程序,并且没有(计划内的)停机时间。

“Snowflake提供符合这些期望的持续可用性。在这方面的两个主要技术特点是故障容错和在线升级。

4.2.1故障恢复

图 2 展示了 Snowflake 架构中各层对节点故障的容忍程度。目前,Snowflake 的数据存储层使用的是 Amazon S3,S3 在多个数据中心(称为“可用区”或 AZ)之间进行复制。在 AZ 之间的复制允许 S3 承受整个 AZ 故障,并保证 99.99%的数据可访问性和 99.999999999% 的持久性。与 S3 架构相匹配,Snowflake 的元数据存储也是分布式且在多个 AZ 之间进行复制。如果一个节点出现故障,其他节点可以继续处理工作而不会给终端用户带来很大影响。云服务层的剩余服务由多个 AZ 中的无状态节点组成,负载均衡器在它们之间分配用户请求。因此,单个节点故障甚至整个 AZ 故障都不会对系统产生整体影响,只是可能会导致当前连接到故障节点上的用户查询失败。这些用户将在下一个查询时被重定向到不同的节点。

解读:

这块可以稍加的解释一下,如果使用云计算比较熟悉的话,会了解云是区分不同Region和不同AZ的(availability zones),Region往往指的是地域,比如北京地域就是北京region、杭州一般就是杭州region、上海就是上海region,然后在不同的region下面区分不同的可用区(简称为AZ),可用区也可以理解为是在不同的region下的不同机房,如果北京可能有万国机房、UCloud机房、亦庄电信机房等等,那么这些就是不同region下的不同可用区的分区。

容错性为什么一定要考虑使用不同的AZ呢?不同AZ也就是要多机房部署的概念,这样当某个机房出现问题的时候,另外一个机房所部署的服务就可以来承接业务,不至于业务出现中断。

一般而言,云上的服务都是多AZ来部署的,比如RDS、Redis等等都是支持跨AZ来部署,所以从段落里重点标记的内容也说明了这一点。

相比之下,虚拟仓库(VW)不分布在多个区域。出于性能考虑而做出此选择。分布式查询执行需要高网络吞吐量,而同一区域内的网络吞吐量显著更高。如果在查询执行过程中其中一个工作节点出现故障,查询会失败,但会自动重新运行,要么立即替换节点,要么暂时减少节点数量。为了加快节点替换速度,Snowflake 维护了一个备用节点的小池子。(这些节点也用于快速提供 VW)。

解读:

上面提到数据存储和元数据是跨AZ存储的,但是VW是部署在单AZ的,为什么?文中给出的是出于性能考虑,分布式查询需要高网络吞吐,而跨AZ之间并不是同一个机房内网通信,需要通过专线进行链接。所以网络延迟相对会比较高(具体要看不同AZ之间距离)。

那么,VW容错怎么来保证呢?首先VW的功能是什么?上面有提到负责查询计划解析、执行等,它是一种纯粹的计算资源,计算资源是一种无状态的,即便是失败了重试之后,也可以在另外一个AZ中进行重新执行。

所以,从整个架构上来看,SQL作业首先会进入到云服务中,云服务会根据元数据信息将作业调度到就近的机房中。

如果整个AZ不可用,那么在该AZ中运行的所有查询都会失败,用户需要主动在另一个AZ中重新配置vw。由于全AZ故障确实是真正灾难性的、非常罕见的事件,我们今天接受这种部分系统不可用的情况,但希望在未来解决这个问题。

4.2.2在线升级

Snowflake 不仅在故障发生时提供持续可用性,而且在软件升级期间也是如此。系统设计允许多个版本的各种服务并存部署,包括云服务组件和虚拟仓库。所有服务都是无状态的,这使得这一点成为可能。所有的硬状态都保存在一个事务性的键值存储中,并且可以通过一个映射层来处理元数据版本控制和模式演变。每当我们更改元数据模式时,我们都会确保与旧版本兼容。

解读:

下面对于在线升级讲的比较清楚了,而且常用的在线升级方式就是多版本共存,新旧感知和业务逐步切换的方式

为了执行软件升级,Snowflake 首先并行部署新版本的服务与旧版本。然后逐步切换用户帐户到新版本,在此期间,每个用户的任何新查询都会被重定向到新版本。所有针对旧版本正在运行的查询都可以完成。一旦所有的查询和用户都完成了对旧版本的使用,该版本的所有服务都将终止和退役。

图3显示了正在进行升级过程的快照。有两个版本的Snowflake并排运行,分别是版本1(浅色)和版本2(深色)。Cloud Services 的一个实例有两个版本,每个版本控制两个虚拟仓库 (VW),每个版本也有两个版本。负载均衡器将传入呼叫路由到适当版本的Cloud Services。单个版本的Cloud Services 只与匹配版本的VW通信。

如前所述,云服务的两个版本共享相同的元数据存储库。此外,不同版本的虚拟机可以共享相同的 worker 节点及其各自的缓存。因此,在升级后无需重新填充缓存。整个过程对用户来说是透明的,不会发生停机或性能下降的情况。

在线升级对我们的发展速度产生了巨大的影响,也影响了我们如何处理 Snowflake 中的关键错误。截至撰写本文时,我们每周都会对所有服务进行一次升级。这意味着我们每周都会发布新功能并改进现有功能。为了确保升级过程顺利,升级和降级都在 Snowflake 的特殊预生产版本中持续进行测试。在这些罕见的情况下,如果我们在生产版中发现了关键错误(不一定是在升级期间),我们可以快速降级到之前的版本,或者实施修复并通过计划外升级来完成。这个过程并不像听起来那么可怕,因为我们一直在测试和锻炼升级/降级机制。它现在已经高度自动化并且经过强化。

 4.3半结构化数据和无模式数据

解读:

半结构化数据是指具有部分结构的数据,通常包含标签或字段名,但不完全符合传统关系型数据库中的表格式结构。而无模式数据则是指没有预定义的数据结构,可以是任意形式的数据,如文本、图像、音频等。在 Snowflake 中,半结构化数据可以通过 VARIANT 类型进行存储和处理,而无模式数据则需要通过外部工具或程序进行导入和转换。

很多NoSQL 数据库有个共同点,那就是它们都没有模式。若要在关系型数据库中存储数据,首先必须定义“模式”,也就是用一种预定义结构向数据库说明:要有哪些表格,表中有哪些列,每一列都存放何种类型的数据。必须先定义好模式,然后才能存放数据。

相比之下,NoSQL 数据库的数据存储就比较随意了。“键值数据库”可以把任何数据存放在一个“键”的名下。“文档数据库”实际上也如此,因为它对所存储的文档结构没有限制。在列族数据库中,任意列里面都可以随意存放数据。你可以在图数据库中新增边,也可以随意向节点和边中添加属性。

Snowflake 在标准 SQL 类型系统的基础上增加了三种半结构化数据类型:VARIANT、ARRAY 和 OBJECT。VARIANT 类型的值可以存储任何原生 SQL 类型(如 DATE、VARCHAR 等)的值,以及可变长度的 ARRAY 数组,以及类似 JavaScript 的 OBJECT 对象,它是从字符串到 VARIANT 值的映射。后者在文献中也被称为文档,从而产生了文档存储的概念

数组和对象只是变体类型的限制。内部表示法是一样的:自我描述、紧凑的二进制序列化,支持快速的键值对查找,以及高效的类型测试、比较和哈希。因此,可以像使用其他列一样,将变体列用作连接键、分组键和排序键。

VARIANT 类型允许 Snowflake 以一种与传统 ETL(提取 - 转换 - 加载)相反的方式进行操作,即 ELT(提取 - 加载 - 转换)。不需要指定文档模式或对加载数据进行转换。用户可以直接将输入数据从 JSON、Avro 或 XML 格式加载到 VARIANT 列中;Snowflake 处理解析和类型推断(参见第 4.3.3 节)。这种方法在文献中被恰当地称为“延迟模式”,它通过解耦信息生产者和信息消费者以及任何中介来实现模式演变。相比之下,在传统的 ETL 管道中,如果数据模式有任何更改,则需要组织内的多个部门协调,这可能需要数月才能完成。

ELT 和 Snowflake 的另一个优势是,如果需要进行转换,可以使用并行 SQL 数据库的全部功能来实现,包括连接、排序、聚合、复杂谓词等操作,这些在传统的 ETL 工具链中通常缺失或效率低下。在这方面,Snowflake 还支持具有完整 JavaScript 语法和变体数据类型集成的用户定义过程(UDF)。对过程 UDF 的支持进一步增加了可以推入 Snowflake 的 ETL 任务数量。

 4.3.1后关系操作(Post-relational Operations)

文档上最重要的操作是从数据元素中提取,通过字段名(对于 对象)或偏移量(对于 数组)。Snowflake 提供了在功能 SQL 表示法 和 JavaScript 风格路径语法 中执行提取的操作。内部编码使得提取非常高效。子元素只是父元素中的一个指针;不需要复制。通常会将提取结果转换为标准 SQL 类型的 VARIANT 值。再次强调,这种编码方式使这些转换变得非常高效。

第二种常见的操作是扁平化,即把嵌套文档转换为多行。Snowflake 使用 SQL 侧视图来表示扁平化操作。这种扁平化可以递归进行,从而完全将文档的层次结构转换为一个适合 SQL 处理的关系表。与扁平化相反的操作是聚合。为了实现这一点,Snowflake 引入了一些新的聚合和分析函数,如 ARRAY_AGG 和 OBJECT_AGG。

解读:

Post-relational Operations 是一种针对半结构化数据的操作方法,它比传统的基于关系模型的数据库操作更为灵活和适应性强。具体来说,Post-relational Operations 包括以下几个方面:

提取数据元素:通过路径表达式或者 JavaScript 类似的语法来提取半结构化数据中的特定字段或者子元素。 扁平化:将嵌套的数据结构转化为平面表,使得数据更容易被查询和分析。 聚合:对半结构化数据进行聚合操作,例如 ARRAY_AGG 和 OBJECT_AGG 等函数,以便更好地进行数据分析和统计。

 4.3.2列存储与处理

将半结构化数据使用序列化(二进制)表示,整合到关系型数据库中是一种常规的设计选择。不幸的是,行式表示法使得存储和处理此类数据的效率低于列式关系数据,这也是将半结构化数据转换为普通关系数据的原因。

Cloudera Impala 21 和Google Dremel 34 已经证明了半结构化数据的列式存储是可能且有益的。然而,Impala 和 Dremel(以及其外部化的 BigQuery 44)要求用户为列式存储提供完整的表模式。为了实现无模式序列化表示的灵活性和列式关系数据库的性能,Snowflake 提出了一种用于类型推断和列式存储的新自动化方法。

如第 3.1 节所述,Snowflake 将数据存储为混合列式格式。当存储半结构化数据时,系统会自动对单个表文件中的文档集合进行统计分析,以自动推断类型并确定哪些(带类型的)路径经常出现。然后从文档中删除相应的列,并使用与原生关系型数据相同的压缩列式格式单独存储。对于这些列,Snowflake 还计算了用于剪枝的显式聚合值(参见第 3.3.3 节),就像普通的关系型数据一样。

在扫描过程中,可以将各种列重新组装为单个VARIANT列。然而,大多数查询只对原始文档中的一组列感兴趣。在这种情况下,Snowflake 将投影和转换表达式推入到扫描操作符中,以便仅访问必要的列,并直接转换为目标 SQL 类型。

上述优化在每个表文件中独立执行,即使在架构演变期间也能实现高效的存储和提取。然而,这确实带来了查询优化方面的挑战,特别是剪枝。假设一个查询有一个基于路径表达式的谓词,并且我们想使用剪枝来限制要扫描的文件集。路径及其对应的列可能出现在大多数文件中,但只有足够的频繁出现才需要元数据出现在一些文件中。保守的做法是对所有没有适当元数据的文件进行扫描。Snowflake 通过计算文档中存在的所有路径(不是值!)上的布隆过滤器来改进此解决方案。这些布隆过滤器与其它文件元数据一起保存,并在剪枝过程中由查询优化器探测。不需要给定查询所需的路径的表文件可以安全地跳过。

4.3.3乐观转换

由于一些原生的 SQL 类型,尤其是日期/时间值,在常见的外部格式(如 JSON 或 XML)中表示为字符串,因此这些值需要在写入时(插入或更新期间)或读取时(查询期间)从字符串转换为其实际类型。如果没有类型化的模式或等效的提示,这些字符串转换需要在读取时进行,这比写入时一次转换效率低,在读取主导的工作负载中。无类型的另一个问题是缺乏适当的元数据来进行剪枝,这对于日期尤其重要。(分析工作负载经常对日期列具有范围谓词。)

但是,如果在写入时应用,则自动转换可能会丢失信息。例如,一个字段可能包含数字产品标识符,但实际上它可能不是一个数字,而是一个带有显著前导零的字符串。类似地,看起来像日期的内容实际上可能是短信的内容。Snowflake 通过执行乐观的数据转换来解决这个问题,并将转换结果与原始字符串(除非存在完全可逆的转换)分开存储在不同的列中。如果稍后查询需要原始字符串,则可以轻松检索或重建。由于未使用的列不会加载和访问,因此对查询性能的影响很小。

4.3.4性能

为了评估列存储、乐观转换和修剪对半结构化数据查询性能的影响,我们使用类似于TPC-H4的数据和查询进行了一组实验。

我们创建了两种类型的数据库模式。第一种是传统的关系型TPC-H模式。第二种是“无模式”数据库模式,其中每个表都由一个类型为VARIANT的单列组成。然后,我们生成了聚集(排序)的SF100和SF1000数据集(分别为100 GB和1 TB),将数据集以纯JSON格式存储(即日期变为字符串),并使用关系和无模式数据库模式将数据加载到Snowflake中。没有给系统任何关于无模式数据的字段、类型和聚类的提示,并且没有进行任何其他调整。然后,我们在无模式数据库上定义了一些视图,以便能够对所有四个数据库运行完全相同的TPC-H查询。(在撰写本文时,Snowflake不使用视图进行类型推断或其他优化,因此这纯粹是一种语法上的便利。)

最后,我们在使用中等规模的标准仓库5上对这四个数据库中的所有22个TPC-H查询进行了测试。图4显示了结果。通过三次热缓存运行获得的结果数字不显著,因此从结果中省略。

如上所述,除了两个查询(在SF1000上的Q9和Q17)之外,无模式存储和处理查询的开销约为所有查询的10%。对于这两个查询,我们确定了导致性能下降的原因是不理想的数据估计中的唯一值估计已知错误。我们继续改进半结构化数据的元数据收集和查询优化。

简而言之,对于具有相对稳定且简单的模式(即在实践中发现的大多数机器生成的数据)的半结构化数据的查询性能几乎与传统关系型数据的性能相当,同时享受列存储、列式执行和剪枝的所有好处——而无需用户付出努力。

4.4时间旅行与克隆

在第 3.3.2 节中,我们讨论了如何在多版本并发控制(MVCC)之上实现快照隔离(SI)。表上的写操作(插入、更新、删除、合并)通过添加或删除整个文件来产生表的新版本。

当文件被新版本删除时,它们会被保留一个可配置的时间(目前最长可达 90 天)。文件保留使Snowflake能够高效地读取表的早期版本;也就是说,在数据库中进行时间旅行。用户可以使用方便的AT或BEFORE语法从SQL访问此功能。时间戳可以是绝对的、相对于当前时间的相对的或相对于之前的语句(由ID引用)的相对的。

SELECT* FROM my_table AT(TIMESTAMP=>

’Mon, 01 May 2015 16:20:00-0700’::timestamp);

SELECT* FROM my_table AT(OFFSET=>-60*5);-- 5 min ago SELECT* FROM my_table BEFORE(STATEMENT=>

’8e5d0ca9-005e-44e6-b858-a8f5b37c5726’);

甚至可以在一个查询中访问同一张表的不同版本。

SELECT new.key, new.value, old.value FROM my_table new JOIN my_table AT(OFFSET=>-86400) old-- 1 day ago ON new.key= old.key WHERE new.value<> old.value;

基于相同的底层元数据,Snowflake 引入了 UNDROP 关键字来快速恢复意外删除的表、模式或整个数据库。

DROP DATABASE important_db;-- whoops! UNDROP DATABASE important_db;

Snowflake还实现了一项我们称之为克隆的功能,通过新关键字CLONE来表示。克隆表会快速创建一个具有相同定义和内容的新表,而无需物理复制表。克隆操作仅复制源表的元数据。在克隆后,两个表都引用相同的文件集,但两个表之后可以独立修改。克隆功能还支持整个模式或数据库,从而实现非常高效的快照。在进行大批量更新或进行漫长的探索性数据分析之前,快照是一种很好的做法。CLONE关键字甚至可以与AT和BEFORE组合使用,允许在事后进行这样的快照。

CREATE DATABASE recovered_db CLONE important_db BEFORE(STATEMENT=> ’8e5d0ca9-005e-44e6-b858-a8f5b37c5726’);

4.5 安全性

Snowflake旨在通过云平台保护用户数据免受所有级别的攻击。为此,Snowflake 实现了双因素身份验证、(客户端)加密的数据导入和导出、安全的数据传输和存储以及数据库对象的基于角色的访问控制(RBAC)。在任何时候,数据在网络上传输之前、写入本地磁盘或共享存储(S3)之前都会被加密。因此,Snowflake 提供了端到端的数据加密和安全性。

4.5.1 主键层次结构

Snowflake 使用强大的 AES 256 位加密,其基于 AWS CloudHSM 的分层密钥模型。[12] 加密密钥会自动进行旋转并重新加密(“重置密钥”),以确保密钥完成完整的 NIST 800-57 密码密钥管理生命周期。[13] 加密和密钥管理对用户来说完全透明,无需配置或管理。

snowflake密钥层次结构,如图 5 所示,包含四级:根密钥、账户密钥、表密钥和文件密钥。每一层(父)密钥加密(封装)其下一层(子)密钥。每个账户密钥对应一个用户帐户,每个表密钥对应一个数据库表,每个文件密钥对应一个表文件。

分层密钥模型是一种很好的安全做法,因为它限制了每个密钥保护的数据量。如图 5 中的方框所示,每一层都减少了其下方密钥的范围。Snowflake 的分层密钥模型在多租户架构中确保用户数据的隔离,因为每个用户帐户都有一个单独的帐户密钥。

4.5.2 关键生命周期

除了限制每个密钥保护的数据量外,Snowflake 还会限制密钥的有效时间。密钥有四个有效阶段:

阶段: (1) 一个预操作创建阶段,(2) 一个操作阶段,在这个阶段中,使用密钥进行加密(发起者使用期)和解密(接收者使用期),(3) 一个后操作阶段,在这个阶段中,密钥不再被使用,以及(4) 一个销毁阶段。第1、3和4个阶段很容易实现。第2步需要限制发起者使用期和接收者使用期。只有当密钥不再对任何必要的数据进行加密时,才能进入第3和第4阶段。Snowflake 使用密钥轮换来限制发起者使用期,并使用重新配钥来限制接收者使用期。

密钥轮换会在固定间隔(例如一个月)内创建密钥的新版本。每次这样的间隔之后,都会生成一个新版本的密钥,并且“退役”前一版本的密钥。退役版本仍然可以使用,但只能用来解密数据。在加密层次结构中的新子密钥或写入表时,仅会使用最新、活动的密钥版本来加密数据。

重新加密 是使用新密钥对旧数据进行重新加密的过程。在特定的时间间隔(例如一年)之后,用过时密钥加密的数据会使用活动密钥重新加密。这种 重新加密 和 密钥轮换 正好相反。当密钥轮换确保密钥从活动状态(源使用)过渡到退役状态(接收者使用)时,重新加密确保密钥可以从退役状态过渡到销毁。

图6显示了一个单表键的生命周。假设密钥每个月旋转一次,每年重新加盐一次。表文件1和2于2014年4月使用k1v1创建。在2014年5月,k1被升级到k1v2,并且使用k1v2创建了表文件3。在2014年6月,k1再次升级为k1v3,并且创建了另外两个表文件。自2014年6月以来,不再向表中插入或更新任何数据。在2015年4月,k1v1过期一年并需要销毁。一个新的密钥k2v1被创建,并且所有与k1v1相关的文件都使用k2v1重新加密。在2015年5月,相同的过程发生在k1v2上,表文件3使用k2v2重新加密。在2015年6月,表文件4和5使用k2v3重新加密。

实现了账户密钥和表密钥之间以及根密钥和账户密钥之间的类比方案。密钥层次结构的每个级别都要进行密钥轮换和重新加密,包括根密钥。账户密钥和根密钥的密钥轮换和重新加密不需要重新加密文件。只需要重新加密下一级别的密钥。

然而,表键和文件键之间的关系是不同的。文件键并不是由表键包装的。相反,文件键是从表键和(唯一的)文件名中派生出来的。因此,每当表键发生变化时,所有相关的文件键都会发生变化,所以受影响的表文件需要重新加密。然而,派生密钥的主要好处是消除了创建、管理和传递单个文件密钥的需求。否则,像 Snowflake 这样的系统必须处理数十亿个文件的数吉字节的文件密钥。

我们之所以选择这种设计,是因为 Snowflake 的存储与计算分离允许它在不影响用户工作负载的情况下进行重新加密。重置密钥的工作是在后台进行的,使用不同于查询的不同的工作者节点。在文件重置密钥后,Snowflake 会原子更新数据库表的元数据以指向新加密的文件。一旦所有正在进行的查询完成,旧文件就会被删除。

4.5.3 完整性保护

Snowflake 使用 AWS CloudHSM 作为防篡改、高安全的方法来生成、存储和使用密钥层次结构中的根密钥。AWS CloudHSM 是一套连接到 AWS 虚拟私有集群 (VPC) 的硬件安全模块 (HSM)。根密钥从未离开过 HSM 设备。所有使用根密钥进行的加密操作都在 HSM 内部执行。因此,如果没有获得对 HSM 设备的授权访问,则无法解密较低级别的密钥。HSM 还用于在帐户和表级别生成密钥,包括在密钥轮换和重新键入期间。我们配置了 AWS CloudHSM 高可用性配置以降低服务中断的可能性。

除了数据加密之外,Snowflake 还通过以下方式保护用户的数据:

通过S3访问策略隔离存储。 在用户帐户中使用基于角色的访问控制,以对数据库对象进行细粒度访问控制。 从云端提供商(亚马逊)处导入和导出加密数据,而云端提供商永远不会看到明文。 为安全访问控制提供双因素认证和联邦身份验证。

简而言之,Snowflake 提供了基于 AWS CloudHSM 的分层密钥模型,并使用密钥轮换和重新键入来确保加密密钥遵循标准化生命周期。密钥管理完全透明于用户,无需配置、管理和停机时间。它是全面安全策略的一部分,可实现端到端加密和安全性。

五、 相关工作

基于云的并行数据库系统。亚马逊拥有多个DBaaS产品,其中Amazon Redshift是这些产品中的数据仓库产品。Redshift起源于并行数据库系统ParAccel,可以说是第一个真正的作为服务提供的数据仓库系统。Redshift使用经典的shared-nothing 架构。因此,虽然可扩展,但添加或删除计算资源需要进行数据重新分配。相比之下,Snowflake的多集群、共享数据架构允许用户独立于存储即时扩展、缩小或暂停计算,而无需进行数据移动,包括跨隔离计算资源集成数据的能力,此外,Snowflake遵循纯服务原则,无需用户进行物理调整、数据整理、手动收集表统计信息或表格清理。虽然Redshift可以将半结构化数据(如JSON)作为VARCHAR导入,但Snowflake具有半结构化数据的本地支持,包括重要的优化,如列存储。

谷歌云平台提供了一个称为 BigQuery[44] 的全托管查询服务,它是 Dremel[34] 的公共实现。BigQuery 服务允许用户以惊人的速度在数千个节点上并行处理数TB的数据。Snowflake 受到启发的原因之一是 BigQuery 对 JSON 和嵌套数据的支持,我们发现这对于现代分析平台至关重要。但是,虽然 BigQuery 提供了一种类似 SQL 的语言,但它与 ANSI SQL 语法和语义存在一些基本差异,这使得它难以与其他基于 SQL 的产品一起使用。此外,BigQuery 表只读写,需要模式定义。相比之下,Snowflake 提供了完整的 DML(插入、更新、删除、合并)操作,ACID 事务,并且不需要模式定义即可处理半结构化数据。

Azure SQL 数据仓库 (Azure SQL DW) 是基于 SQL Server 和其分析平台系统 (APS) 设备的 Azure 云平台及服务的最新加入[35][37]。与 Snowflake 相似,它分离了存储和计算。通过数据仓库单位 (DWU) 可以扩展计算资源。然而并发度有限制。对于任何数据仓库,同时执行查询的最大数量为32[47]。相比之下,Snowflake 允许通过虚拟仓库对并发工作负载进行完全独立的扩展。Snowflake 用户也不必再为选择合适的分布键和其他管理任务而烦恼。虽然 Azure SQL DW 支持通过 PolyBase[24] 对非关系型数据执行查询,但它没有像 Snowflake 的 VARIANT 类型及其相关优化那样内置支持半结构化数据的功能。

文档存储与大数据。近年来,MongoDB[39]、Couchbase Server[23] 和 Apache Cassandra[6] 等文档存储在应用程序开发人员中变得越来越受欢迎,因为它们提供了可扩展性、简单性和模式灵活性。然而,由于这些系统简单的键值对和 CRUD(创建、读取、更新和删除)API,表达更复杂的查询是一项挑战。为此,我们看到了诸如 Couchbase 的 N1QL[22] 或 Apache Cassandra 的 CQL[19] 等多个类似 SQL 的查询语言的出现。此外,Apache Hive[9]、Apache Spark[11]、Apache Drill[7]、Cloudera Impala[21] 和 Facebook Presto[43] 等许多“大数据”引擎现在支持嵌套数据上的查询。我们认为这表明了对针对无模式或半结构化数据进行复杂分析的实际需求,而我们的半结构化数据支持受到了其中许多系统的启发。通过使用模式推断、乐观转换和列式存储,Snowflake 将这些系统的灵活性与关系型数据库的存储效率和执行速度结合了起来。

六、 学到的经验和展望

当Snowflake于2012年成立时,数据库世界完全专注于Hadoop上的SQL,短短时间内出现了十几个系统。当时,在一个完全不同的方向上工作,构建一个“经典”的云数据仓库系统的决定似乎是反潮流和冒险的举动。经过三年的发展,我们相信这是正确的选择。Hadoop并没有取代关系型数据库管理系统;它只是补充了它们。人们仍然想要一个关系型数据库,但更高效、灵活,并且更适合云计算。

解读:

不得不说大数据技术的发展是应用了分布式技术支持大批量数据的计算和存储工作,并不是完全颠覆式的存在,而snowflake这种云数据仓库的思路是依赖于云计算的崛起,在云能力之上补充了数据结构的不足以及成本效益的考虑下所作出的创新式技术。

Snowflake 满足了我们对云原生系统为用户和开发者提供的期望。多集群共享数据架构的弹性能改变用户处理数据任务的方式。SaaS 模式不仅使用户能够轻松试用和采用该系统,而且极大地帮助了我们的开发和测试工作。通过一个单一的生产版本和在线升级,我们可以更快地发布新功能、提供改进并修复问题,这比传统的开发模式要快得多。

虽然我们希望半结构化扩展会有所帮助,但我们对采用的速度感到惊讶。我们发现了一个非常流行的模型,组织使用Hadoop来完成两件事:一是存储JSON,二是将其转换为可以加载到RDBMS中的格式。通过提供一个系统,该系统能够高效地存储和处理非结构化数据——并且具有强大的SQL界面——我们发现Snowflake 不仅取代了传统的数据库系统,还取代了Hadoop集群。

解读:

目前来看,近几年数据湖技术开始流行,也正是验证了snowflake当初的设想,某种意义上是能够替代Hadoop集群的。无论是从成本还是组件兼容性都可以做到Hadoop集群更好的实现。

当然,这并不是一帆风顺的旅程。虽然我们的团队拥有超过100年的数据库开发经验,但我们在路上还是犯了一些可以避免的错误,包括对一些关系运算符过于简单的早期实现、没有在引擎一开始就包含所有数据类型、没有及早关注资源管理、推迟了全面日期/时间功能等。此外,我们持续专注于避免调整旋钮也带来了一系列工程挑战,最终带来了许多令人兴奋的技术解决方案。因此,今天,Snowflake 只有一个调优参数:用户想要多少性能(并愿意为之付费)。

虽然Snowflake的性能已经非常有竞争力,尤其是考虑到不需要调优方面,但我们知道还有很多优化工作我们还没有时间去做。然而有点出乎意料的是,核心性能几乎从未成为我们的用户的问题。这是因为虚拟仓库提供的弹性计算可以偶尔提供所需的性能提升。这让我们把开发重点放在了系统其他方面。

我们面临的最大技术挑战与该系统的 SaaS 和多租户方面有关。构建一个可以支持数百个用户并发访问的元数据层是一项非常具有挑战性和复杂性的任务。处理各种类型的节点故障、网络故障和支持服务是一场永无止境的战斗。安全一直是个大问题,而且还会继续存在:保护系统和用户的数据免受外部攻击、用户本身以及内部用户的侵害。维护一个由数百个节点组成的在线系统,每天运行数百万次查询,虽然会带来很多满足感,但需要高度集成的方法来开发、运维和支持。

Snowflake用户继续向系统提出越来越大的、越来越复杂的问题,影响着它的演变。我们目前正在通过提供额外的元数据结构和数据重组任务来改善对数据的访问性能,重点是最小化或无用户交互。我们继续改进和扩展核心查询处理功能,包括标准 SQL 和半关系型扩展。我们计划进一步提高倾斜处理和负载平衡策略,随着我们的用户工作量的增加,它们的重要性也在增加。我们致力于简化工作负载管理,使系统更具弹性。我们还致力于与外部系统集成,包括高频数据加载等问题。

Snowflake 面临的最大未来挑战是向完全自助模式过渡,用户可以在任何阶段注册并与系统交互,而无需我们的参与。这将带来许多安全、性能和支持方面的挑战。我们期待着这些挑战。

七、 致谢

Snowflake是太多人的作品,无法在此列出。我们要感谢整个Snowflake工程团队对产品的贡献,以及他们为此付出的辛勤工作、努力和自豪。我们还要感谢所有其他“Snowflakes”,感谢他们出色的完成了这项工作,将这款产品带给用户,并一起打造一家伟大的公司。与这样优秀的团队共事,我们感到无比荣幸和谦卑。

相关阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。