# 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 是临时性的,所以需要写入到存储
- 
## 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 认为它是一个最先进的系统,但是许多实现并没有公开。