Snowflake 架构讨论 《The Snowflake Elastic Data Warehouse》
snowflake 随谈
随想
今天,如果从事大数据或者OLAP 领域的朋友,都 应该仔细阅读一下snowflake 的论文。 snowflake 是前年看的, 但一直想写一篇介绍的读后感,一直没有下笔, 放在心中, 成为一个疙瘩, 终于在新年新气象的号召下, 终于把当初的阅读笔记找出, 梳理一遍, 列一下snowflake 中,很多有意思的东西。
论文原地址 :The Snowflake Elastic Data Warehouse
架构
snowflake 的架构呈现出来时, 还是很让人眼前一亮, 这一套架构, 吸收了传统数据库mpp 的架构的优点, 有吸收了云上DLA 架构(serverless)的优势, 整体是一套对云计算非常友好的olap 系统。 这一套系统比传统大数据(hadoop系列/emar) 要轻量高效, 比传统的mpp数据库(on-premise的云数据库)又要性价比高, 更便宜更灵活。
整套系统是完全部署在云上, 所有的计算节点和service 都是购买虚拟机, 而存储是使用aws 的s3或微软的类s3的对象存储。 整套架构是share disk 的架构, 并且设计了一套都有cache机制,让整个计算层是无状态的, 所有有状态的东西存储到kvstore中, 而前端节点采用微服务, 保障系统的可靠性又让系统的成本最低化。
计算
计算层有个query process pool, virtual warehouse 就是一个用户独享的cluster, 这个virtual warehouse 由几个ec2 来组成。 这个cluster 的规格就由用户购买规格来决定的, 用户购买规格类似购买t-shirt 一样, 就只有xxs –> xxl 等几个规格, 用户不需要涉及多少个cpu/内存/本地存储等等一系列概念, 非常简单直接, 而且每种规格是以分钟计费。
当扩容后, 下一分钟后的新sql或者队列中的sql 就可以运行在扩容后virtual warehouse。
当没有query时, 可以将virtual house 进行自动挂起(不过计算单位还是分钟), 当query 来时, 自动resume。 (这样的功能,更加证明)
启动一个session时, 并没有绑定一个virtual warehouse, 直到一个virtual warehouse 绑定到一个session后, 这个session的所有sql才能到virtual warehouse上执行。 一个session只能绑定一个virtual warehouse, 但一个virtual warehouse可以绑定多个session, 甚至绑定一个user, 这样这个user的所有session自动绑定到这个virtual warehouse。
不同版本的virtual warehouse 可以使用相同的ec2, 因此可以共享cache。
未来snowflake, 可以进行share ec2.
根据上面提供几点功能, 我对背后架构的一个解读。
- 能提供这么强的弹性, 第一种方式是类似hadoop 这种大集群架构(先表示对论文提到的virtual warehouse 是有ec2 来组成保持一点怀疑,因为大集群方式通常是成本最低的), 部署一个超大的hadoop, 来了一个sql, 就分配一批docker 来为这个sql 进行服务, docker 隔离了 cpu和内存。
- 第二种方式, 是以虚拟机为调度单元, 有请求时,将用户设定大小的虚拟机集群划给用户, 当没有请求时, 这些虚拟机分配给其他的用户。
从论文的描述来讲, 更倾向于第二种架构, 第一,在云上要保证足够的安全性和隔离性 使用虚拟机会更安全, 但一般来讲,虚拟机是无法达到如此高的灵活性。 但snowflake 做一些投机取巧的事情, 第一, 他是以分钟为单位进行计费, 1分钟足够将一个虚拟机划给另外一个客户。 第二,在数据库领域很少出现一分钟内没有任何请求的case, 很多bi 工具或客户段, 他们都会定时发送请求过来, 激活链接。
在基于virtual warehouse由ec2来组成后, snowflake 有一个非常powerful的调度系统, 并且随时在系统中保留了几个hot的ec2, 随时让这几个ec2 扩容到需要扩容的virtual warehouse中。 并向前再推导一步, ec2 的规格应该非常少, 不会超过3种, 这样就能很方便的将一个ec2 从一个virtual warehouse 迁移到另外一个virtual warehouse。
另外, snowflake的优化器和执行器 提供一个很强的扩展性, 比如在1000个节点可以运行, 在10个节点也能运行, 只是时间被拉长。 这样印证了系统。
snowflake 就支持xxs –> xxl 这些规格, 这样就将主流用户把握住, 避免浪费精力在那种超级大客户上, 这种超级大客户(规模超过xxl)很多时候, 他们需要的是服务, 而不是产品,甚至比拼的是销售。 而且技术上要为这些超级大客户备机器也是一件拉高成本的糟糕事情。
存储
- 目前的设计, 每个virtual warehouse的cache 是不能共享。
- 每个ec2 都有一个本地存储, 当读取数据时,都是从s3 中将数据捞到本地存储中, 当写入数据时, 如果本地磁盘满,会将结果临时存储到s3上, 保障写通畅。
- 每个ec2 上的cache 采用lru 算法。 当扩容时, 采用一致性hash, 如果数据已被cache住,就直接读取, 如果因为扩容, 数据在一致性hash后,飘到其他机器上, 没有关系,重新捞取数据,然后利用lru的自动淘汰算法,将老的数据全部淘汰换成新的。
- 存储格式上, 使用了列村, 并且对列村进行压缩和优化过, 对用户不可见。
- 每张表被分成大小不变的file (这样降低s3 的成本), 将每个列的元数据信息存储公共的元数据服务里面。
- 支持半结构化数据, json, avro 格式数据
- 支持acid – 事物, 号称支持snapshot isolation。
- 支持回收站, 跨region backup, 支持clone。 (以来share disk 和cache 的无状态设计)
- 对热点文件做了一个优化, 使用了file stealing技术, 当一个peer 发现他的peer 节点还有文件没有读取, 改变文件的ownership 在当前query下, 这样这个peer帮助忙节点完成一些计算工作。 (这也是为什么s3 中文件都是固定大小)
- 没有事物引擎, 也没有buffer pool
分析:
这种share disk和cache 的设计,让整个virtual warehouse 处在一个无状态的状态下,因此,调度器可以随时将一些ec2 切走。 另外, 因为定位的是olap系统,不是oltp系统, 对 请求的失败或时延没有那么高的要求。
当数据写入时, 可以通道直接打通到virtual warehouse, 但作者没有介绍,如何解决故障问题, 比如当数据写入到s3 前,写入节点的ec2 发生故障, 怎么保障数据一致性。 因为数据是share disk架构, 并且系统没有做类似paxos 的3节点日志, 系统应该是通过提供一个事物来支持这种failover, 当写入s3 成功后,才返回事物成功。
优化器& 执行引擎
- 号称没有使用index (一种怀疑是全列存架构)
- 推迟执行, 减少optimizer的错误
- 持续收集query state, performance counter, detect node fail
- 执行引擎支持vectorize – simd – 也说明他们底层实现是c/c++ 语言
- 做了大量的下推操作,
- 没有采用pruning 技术, 在oltp中,随机访问很场景, 因此使用b+ 树非常多, 但在s3中,并且大量使用压缩的情况下, pruning 很多时候没有什么效果,需要采用其他的技术,
- min-max based pruning, 做区间判断是否可以跳过文件。 (比如join时, 在build table时,收集信息, 然后在probe时,可以跳过一个不匹配的文件)
- small materialized aggregate
- zone map
- data skipping
common service
- 所有的service 是无状态的, 随时可以升级,扩容
- hard state 存储到kv store中, kv store 也是通过mapping layer来访问, 使用metadata version, schema evolution, 保障向前兼容。
- 采用微服务的这种架构, 让系统扩展性非常好,并且节省了成本。
半结构化数据
可以将数据从json, avro, xml, load 到variant
variant 自描述, 压缩binary 序列化, 可以快速kv 查询, 高些test, hash 笔记, schema 可以自我进化。
udf 支持javascript, udf 支持variant, 以后支持存储过程。
impala 和dremel 都使用完整的table schema ,从而支持半结构化的data, snowflake 用了一种新的自动类型探测和列式存储。
当存半结构化数据是, 对table file 进行statics 分析, 自动类型探测, 决定哪种type , 然后对某些列从原始文档中删除,然后用相同格式和压缩方式进行单独存储, 这些列通过物化视图来访问。
分析:
这一段吹的神乎其神, 应该是解决了某几个场景,但个人觉得对半结构化支持, 应该牺牲了性能来做。
像论文里面提到一种方法flatten, 旋转nested document到多行, 用sql lateral view来展示flatten的操作。
mvcc
- 一个历史文件默认保留90天
- 有一个time travel, 读取一个历史版本 (timestamp xxx before)
- 回收站
- clone, 不做物理clone, 只是metadata clone,做snapshot非常方便。 两个table 可以独立修改。