Snowflake是目前话题度超高的云原生数仓产品,从20年下半年上市到现在已经市值千亿了。它的流行进一步印证了云的重要性。纵观现在大大小小的数据库厂商,上云是必然要走的战略步骤,而snowflake则更加直接,类似于AWS Aurora或我们的PolarDB,它就是围绕着云基础设施构建的OLAP数据库产品。
前言
这篇paper发表于2016年,对snowflake的设计理念和整体架构做了整体的介绍:
- 多租户,弹性,全面SQL支持,ACID事务,半结构化数据内置支持,高度可扩展,高可用
- 设计本身就是希望做到Software as a service,具有最少的配置和用户运维(很重要!),甚至不需要做physical design
- multi-cluster + shared data
- 计算存储分离,各自独立扩展
传统的share nothing数仓架构存在一些问题:
- 数据和计算耦合,所有的机器是同构的,但workload却是异构的,有些是IO密集的,有些是CPU密集的,但为了处理所有这些任务,每个node都要在多方面有好的配置,导致资源利用率下降,这个看下TiDB的机器配置就能看出来了。
- 成员变更导致数据reshuffle,可能会影响节点的性能,造成集群性能抖动。
- online upgrade较困难,各个节点都只有部分数据,升级需要节点间协同来避免数据处理不一致。
后两点在云端是很重要的,因为经常有节点失效,而且高效的升级有利于缩短业务开发的周期,提升企业的业务竞争力。
因此Snowflake将计算和存储解耦成了两个service,可以各自独立扩展:
- 计算层是share nothing的virtual warehouse,由EC2的cluster组成,每个EC2是一个worker node,包含本地的Local disk作为table data cache.
- 存储层是AWS S3块存储,可以认为拥有无限容量+不会丢数据+超低成本。
两者结合形成了multi-cluster + shared data的架构。
整体架构
分为3层,从上到下是Cloud Servier -> Virtual Warehouse -> Data Storage,各层间通过Restful API进行交互。
Data Storage
AWS S3块存储,具有极好的可用性和持久性,就是云盘,其特点是访问延迟较高,但吞吐量很大。通过PUT/GET/DELETE操作,对file只能整体写入/覆盖,不能做append操作,这个特点也极大影响了snowflake在并发控制等方面的设计。(data file是不可变的!!) 但是GET可以获取部分数据,因此可以只获取某些列。
table被分区为若干的micro partition,每个MP对应一个file,内部按列进行组织+压缩+存储,file header中存储了各个列的offset,便于部分读取,和Parquet的格式类似。
query中生成的temp data也可以持久化到S3中(VW local disk不够用时),因此理论上没有OOM/OOD的问题。
在Cloud Service(FoundationDB)中,存储了集群中最重要的:table -> files的关联,file统计信息(zonemap),trx lock/trx log,schema,访问控制等元数据信息!
Virtual Warehouse
是针对特定用户的对计算资源的抽象,本身是share nothing EC2集群。一个snowflake部署可以创建多个VW,各自针对不同的workload按需创建,各自独立scale up/down,因此可以充分利用云上的资源弹性。
VW是无状态的,失效的话,可以做query level的retry。
每个worker node有自己的local cache(本地SSD),保存其所访问数据的file header + column data,cache在worker node内部,会被多个worker process共享,通过简单的LRU机制实现replacement。
当一个query要执行时,有上层(Cloud Service)的query optimizer,对于query要访问的数据,通过consistent hashing将table data files分配到VW的各个worker中,形成一个暂时的share-nothing集群。
注意:这种consistent hashing是lazy的!worker process只确定自己所负责的data files集合,但不会立即去S3获取数据,而是借助LRU的策略,按需读取,逐渐替换掉local cache中的data,因此即使resizing也比较平滑。
针对负载skew问题,通过允许在query level做file stealing,由高效的worker node从低效的worker node中获取file进行scan和处理,实现负载均衡。
执行引擎的特点是:
- 面向列 (压缩 + SIMD + cache高效)
- 向量化执行 (小批量流水线)
- push based (避免iterator中那种tight loop,此外可以允许DAG形态的计划,一份数据推入到多个下游operator)
- 没有transaction处理的overhead,全在上层
- 没有buffer pool !!! 内存完全用于对算子的计算
Cloud Service
这层是面向所有用户的(多租户),提供了很重要的系统管理功能 : 访问控制,query optimization, 事务管理,schema,table -> files映射。使用了FoundationDB做跨AZ复制,保证高可用性。
- query optimization
使用了Cascades CBO,但由于没有index(列存),search space要小很多。不维护index,使用zonemap实现对数据access的限制,zonemap中保存精确的统计信息。不做过多的优化,一些决策在运行时决定,避免产生很糟糕的计划,保证性能可预期。
- 并发控制
基于底层S3 Copy-On-Write的特性,每个table file只要变更,就会产生一个新文件,同时构成新的table version,建立新的metadata维护新的映射,因此随机更新效果很差,bulk load / bulk update比较适用。
这种COW天然就是MVCC,支持SI。
table version的演进保存在FoundationDB中。
- Zonemap pruning
由于不可变的table file可以得到精确的统计信息,因此不使用难以维护的index,而是用zonemap尽可能实现数据的过滤,减少data access。
zonemap的方式很适合COW,每产生新的file,就创建新的zonemap,zonemap很小,存储overhead很低,也可以快速访问。
除了static pruning,还尽量做dynamic pruning,比如build hash table后,会收集join key的分布信息,并push到probe侧进行pruning。
关于snowflake如何做优化可以看下cmu的这个talk:
https://www.youtube.com/watch?v=CPWn1SZUZqE
持续可用
在system层,使用了跨AZ复制的metadata storage : FoundationDB。
在data层,使用S3的data storage。
这两个系统都具有高度的可用性,因此保证了整个系统的状态是高可用的,而其他的节点,无论是Cloud Service的各个service node,还是不跨AZ的Virtual Warehouse,都是stateless的,失效的话,在上层做query的重试即可。
Online upgrade
正是由于各个node是stateless,更新就非常简单:
Metadata storage如果有schema的变更,一定要后向兼容。同时利用FoundationDB快速failover快速启动recovery的特性,可以平稳的升级到一套新实例版本。
service node等,直接可以部署新的serivce并逐渐引流就可以了。
Time travel + Cloning
由于MVCC + COW的file,老版本table的file可以在S3中保留一段时间,因此可以通过指定snapshot ts的方式,实现time travel的查询。
而且可以利用S3的存储特性,UNDROP掉误删除的数据(数据并不丢弃)。
此外,由于COW,可以做table clone,也就是基于已有的table definition + table data,复制一个新表,可以想像,这个表并不需要复制数据,只复制metadata就够了,后续两套metadata独立演进,类似git branch。
总结
这篇paper非常high level,因此技术细节比较模糊,但从中反而可以看到snowflake一些最为重要的产品设计决策,个人感觉正是因为这些决策造就了它现在的成功。
- 充分利用云基础设施,尤其是弹性的计算资源 + 无限扩张且稳定又成本低廉的存储资源,再结合多租户的特性,可以尽量降低实例的部署和运维成本。
- 不追求极致性能,可以看到这种Virtual Warehouse的架构和传统的share nothing数仓(Redshift/Greenplum)是很不一样的,基于S3 -> SSD -> Memory的跨网络存储层级估计很难有很好的综合查询性能,除非workload比较固定,相关数据始终cache在VW的各台EC2中。
- 系统很强调易用性,尽量避免用户调节的开关和用户维护,这个感觉非常重要,云上用户非常重要的一点就是降低上手门槛和运维工作量,个人感觉这个应该是除了成本外客户最关心的问题,至于query response time这些,AP场景下应该还好。
- 对Semi-Structured data 和 Schema-Less data的支持。
Snowflake这种特殊的计算存储分离的云原生形态对很多数据库厂商产生了不小的影响。例如MemSQL商业化后的SingleStore,思路和这个很接近,只不过由于它们原本是share nothing的架构,只能是从local cache -> S3这样异步同步数据来降低存储成本和实现本地热数据缓存。TiDB的目前貌似也在研究类似的问题,即通过底层大规模对象存储降低集群成本。