上篇文章Snowflake (三)说了VM的事儿,这篇说云服务层Cloud Service的那点事儿
云服务层
相比于虚拟仓库(VM)层的用户独享资源设计来说,云服务层是实打实的“多租户”设计了,毕竟对于出现在云服务层的权限管控、查询优化、事务管理等,都会在云服务层为多用户共享提供的。多租的好处就是节省,毕竟不再需要像原先一个用户部署一套系统那样浪费资源。当然了,为了保障服务的稳定性,每个服务都是高可用的,单服务节点挂掉不会影响数据的丢失,当然了,当时运行在这台机器上的查询可能会失败,这事儿可没法保证。
特性一:查询管理和优化
关于查询的前置步骤,都是在这一层完成的,包括我们所熟知的语法解析,词法解析,优化,权限管控等等。优化,是所有SQL逃不掉的必经之路,也是重点要解决的问题。
Snowflake的优化使用的是CBO,全称Cost-Based Optimization。这种优化思路需要考验对于计算数据的统计信息,以最终计算消耗最小代价来决定执行计划的优化方式。在数据load和update的时候,Snowflake会自动收集相关的统计参数,用于代价分析。除此之外,Snowflake充分运用了一些商业思维,许多决定是在执行前才开始确定。这样的方式可以使得早先无需花更多的时间去设计执行计划,而且做出来的计划更稳定可靠。
当优化完成时,生成的可执行计划会发送给不同的worker进行对应数据的处理,而此时云服务层也会开始统计各个节点的执行情况和失败情况。这些信息会用于审计和分析运行指标,用户也可以通过可视化界面进行查看。
特性二:一致性保证
一致性是个很有意思的词。大家都知道,不论是hdfs还是s3,都是不支持文件变更的,要写就写个新的覆盖,而不是在源文件上写。这样的设计有一个好处就是不需要管理每一个文件的锁,彻底简化了文件管理的逻辑,也避免了多线程同时写入的风险。
Snowflake设计了Snapshot Isolation,简称SI,用于实现ACID事务。在这样的设计中,所有的读操作,其实都是读的当前文档的一个快照。至于什么是“当前”,Snowflake还设计了一个“多版本一致性保证”,简称MVCC,说白了,就是保存一个库在一段时间内的多个版本控制。
因此,当用户试图修改文件,包括写、更新、删除、移动等,都会在统一的元数据库中(K-V结构)进行记录,根据对应的表个当时的version,用户可以很容易的进行文件确定和处理,也就保证了一致性。
当然了,Snowflake也将这些Snapshot用于实现time travel,或者是快速复制数据库等操作。
特性三:剪枝
传统的数据存储是依赖分片存储的,例如使用B+tree等数据结构。虽然这种方式在针对事务的处理上是非常有效的,但是也给数据查询平台例如Snowflake带来了非常大的麻烦。
- 首先,这种方式非常依赖随机读,但是对于S3和压缩存储来说,这是个很大的问题,随机读无法带来效率,反而带来麻烦。
- 维护数据分片会大大增加数据存储内容,以及数据的加载时间
- 用户需要显示的设计分片,这与Snowflake的用户无感知不需要专业等理念存在矛盾
因此Snowflake认为,使用和维护数据分片是非常有风险、需要高深知识积累等才能完成的事件,换句话说,尽可能不要使用的技术。
于是,Snowflake关注到了最近的“min-max剪枝算法”。与small materialized aggregates, zone maps和data skipping是差不多的内容。算法的大意是,服务端会保存数据的粗略信息,包括最大最小值,位置,总records数量等,数据分析时可以根据分析结果预测所需读取的相关数据,而不需要通过随机读读取不需要的数据。
举个例子:假设文件f1中记录对于column X的数据从3-5,f2中记录数据为4-6,那么在分析时计算出需要数据为x>=6,那就仅仅需要读取f2的数据即可。
保存元数据所需要的存储空间非常少。Snowflake维护了每一个Table的相应信息,除了普通的列之外,还有一些自适应推导出的半结构化信息。当然,除了静态的剪枝,Snowflake还实现了动态剪枝。例如在进行hash join时,会通过收集构建侧的过滤信息,通知读取数据节点,进行一部分数据的跳读。这个玩意儿在专业术语中叫谓词下推。Snowflake通过这样的设计,补充了当前很多调优join算法的实现,例如Bloom join。