# 1. Historical Context - 2000年代见证了几个 special-purpose 关系型 OLAP engine 的崛起: - Vertica,Greenplum,MonetDB,Vectorwise,ParAccel - 2010年代早期,许多组织尝试在 Hadoop / HDFS 上使用 SQL - Hive,Presto,Impala,Stinger - 所有这些系统都是 self-managed / on-prem - Google 的 Dremel paper 在 2011 年出现 - Facebook 在 2012 年开始构建 Presto - Amazon 在 2011 年获得 ParAccel 许可,并且在 2013 年作为 Redshift 在 AWS 上发布。 - SutterHill VCs 招募了两名 Oracle 工程师(Dagevile,Cruanes)和 Vectorwise 的联合创始人(Zukowski)在 2012 年开始构建 Snowflake。 ---------- # 2. Snowflake - C++ 编写的 Managed OLAP DBMS - Shard-disk 架构 以及 积极的 compute-side local caching - 从零开始编写。没有借用现有系统的组件。 - 自定义 SQL 方言、client-server 网络协议 - [[snowflake-paper |Paper]] - [Guest lecture](https://www.youtube.com/watch?v=dABd7JQz0A8) ## 2.1 Key Features - Shared-Disk / Disaggregated Storage - Push-based Vectorized Query Processing - Precompiled Primitives - Separate Table Data from Meta-Data - No Buffer Pool - PAX 列存 - 同时支持私有和开源格式 - Sort-Merge + Hash Joins - Unified Query Optimizer + Adaptive Optimizations ## 2.2 Architecture - **Data Storage**:云上对象存储 - S3、MSFT Azure Store、Google Cloud Storage - **Virtual Warehouses**:Worker nodes - 运行 Snowflake software 的 VM instance,带有用于 caching 的 local disks - 用户指定 compute capacity - 2022 年支持 serverless 部署 - **Cloud Services**:Coordinator / Scheduler / Catalog - 元数据存储在事务型 KV 存储(FoundationDB) ## 2.3 Execution - **Woker Node**(如 EC2 Instance) - 维护 files + columns 的 local cache(worker processes 之前访问的数据) - 简单的 LRU 置换策略 - Optimizer 使用一致性 Hash 将各个 table files 分配给 worker nodes,保证一个文件只缓存到一个位置上。 - **Worker Process**(如 Unix Process) - 查询执行期间创建 - 可以推送中间结果给其他 Worker Processes 或者 写入到存储 ### Vectorized Query Processing - Push-based vectorized engine,operator kernels 使用了 precompiled primitives - 使用了 C++ 模板为不同的 vector data types 预编译 - workers 之间的 tuple 序列化/反序列化使用了 codegen(通过 LLVM) - stages 之间不依赖 shuffle step - worker process 可以 push data 到彼此 - 不支持 partial query retires - 如果一个 worker 失败了,那么整个 query 必须 restart ### Work Stealing - 在执行前,optimizer 决定 wokers 去获取哪些文件 - 当一个 woker scan 完了它自己的 input files 后,它可以向 peer worker process 请求替它扫描 - 直接从 storage 上 **download**,而非向 peer 获取文件(避免使 peer 变得更慢) - stolen files 不会缓存(只是临时性的) ### Flexible Compute ^9be5b8 - 如果某个 query plan fragment 需要处理大量数据,那么 DBMS 可以为它临时性地部署额外的 worker nodes 来加速执行 - Flexible compute worker nodes 将结果写入到 storage(类似一张表) - 这些 nodes 是临时性的,所以需要写入到存储 - ![400](https://img.jonahgao.com/oss/note/2025p1/16721_2023_snowflake_flex.png) ## 2.4 Storage - [Building an elastic query engine on disaggregated storage](https://dl.acm.org/doi/10.5555/3388242.3388275) - Cloud object storage 比 local disk 慢,以及每个 IO 的 CPU 开销更高(需要 HTTPS API calls) - 但是 cloud storage 支持按 offsets 读取文件 - DBMS 先获取 header,然后再决定读取文件内的哪部分数据 - Snowflake 大力投资自己的 caching layer 来 hide latencies。 ### Storage Format - Snowflake 基本上使用内部的列存格式存储所有的 tables,将它们拆分成 **micropartition** files - immutable files,PAX 格式 - 每个 micropartition 原始大小 50-500 MB,压缩后一个文件可以到约 16 MB - 根据 query access parttens 自动地在后台对 micropartition 进行 [clusters](https://docs.snowflake.com/en/user-guide/tables-clustering-micropartitions#clustering-information-maintained-for-micro-partitions) 和 re-arrangs。 - Snowflake 提供了自定义的数据类型来存储半结构化数据 - VARIANT,ARRAY,OBJECT - Load 数据时自动推断 JSON/XML fields 的数据类型(写时 schema) - 例如:将字符串 ”2023-04-18“ 识别为 4 字节 DATE - 需要始终保留原始未解析的数据(防止推断错误) ### Consistent Hashing - DBMS 使用一致性 hash 将 micropartition files 映射到 worker nodes 上。 - 映射是事务性的,这样所有的工作节点都能同步地知道哪个节点负责哪些文件 - 确保访问相同 microparition 的 query fragments(tasks)分配到相同的 worker nodes。 - 添加新 compute nodes 允许不变更 microparition 的分配 - 避免本地 cache files 的失效 ## 2.5 Query Optimizer - Unified Cascades-style top-down optimization - Snowflake 将它们的 optimizer 称为 “compiler” - 在查询开始执行前,Optimizer 检查 catalog 进行 **prune** microparitions - 确定一个 pipeline 访问多少个 micropartitions,有助于确定 query 的复杂度,决策 [[21-snowflake#^9be5b8|Flexible compute]] - DBMS 同时也支持 query plan hints 和 runtime adaptivity。 ### Statistics Collection - DBMS 将统计信息存储在 Snowflake 的私有 table format - 只有简单的 zone maps,没有 histograms / sketches - 当使用内部文件格式(micropartitions)时,统计数据与数据同步。 - Table + Microparititions 两个级别的统计信息: - row 的数量、字节数(和压缩信息一起) - Columns:(列的统计信息) - Min / Max, Null / Distinct counts ### Pruning - 基于 统计信息 来决定 skip 哪些 micropartitions - Statistics 缓存在本地,来加快优化速度 - pruning 时支持计算复杂的表达式 - 需要特殊的 expression evaluators,支持操作 zone map 信息 - 还需要考虑 null indicators - cloud service 负责(非 worker nodes) ```SQL SELECT * FROM xxx WHERE col1 + col2 > 1234; ``` ## 2.6 Ecosystem - 问题:外部创建的数据缺少统计信息 - Snowflake 原则上要求用户 load 所有数据到 DBMS 中 - Snowflake 扩展了它的架构来支持更多的 data ingesting 方法 - Snowpipe(通过 Apache Arrow) - [External Tables](https://docs.snowflake.com/en/user-guide/tables-external-intro)(2019) - Hybrid Tables(2022) ### Apache Iceberg(2017) - Infrastructure、Parquet 的 文件格式扩展(为 object store 中的数据文件维护 catalog) - 维护 partitioning、versioning 和 schema changes - 提供 catalog service,用于 runtime lookups 和 pruning meta-data - 2021 年, Snowflake 支持了 ingesting、creating 和 querying Iceberg files。 ### Hybrid Tables(2022) - 新的 service :[Unistore](https://resources.snowflake.com/external-content/snowflakes-new-unistore-workload-and-hybrid-tables-demo),可以直接在 Snowflake 中支持 OLAP workloads。 - 用户将 table 声明为 hybrid(row + columnar) - 写入到 row-based storage 提供了强事务保证 - 后台任务将它们 merge 成 microparition files - OLAP 查询同时从 row-based 和 columnar storage 中获取数据,然后合并结果。 ### FoundationDB - Snowflake 的 catalog service 使用了 foundationdb。 --------- # Parting Thoughts > Snowflake created the roadmap on how to build a scalable cloud-based OLAP DBMS as a service. - Andy 认为它是一个最先进的系统,但是许多实现并没有公开。