土法炼钢兴趣小组的算法知识备份

【数据库研究前沿】HTAP 新范式:从 TiDB、SingleStore 到 Lakehouse 一体化

文章导航

分类入口
database
标签入口
#htap#tidb#singlestore#f1-lightning#lakehouse#ch-benchmark#hattrick

目录

把一份在线交易(OLTP)刚写下的订单,在下一秒就纳入全公司的实时报表(OLAP),这是企业数据平台近十年反复讨论的问题。传统架构是两条管线:OLTP 系统处理写入与点查,通过 CDC(Change Data Capture)把变更异步送到 OLAP 仓库;两边各自为政,报表通常滞后分钟级到小时级。HTAP(Hybrid Transactional/Analytical Processing)的目标是把这两段路径合而为一:同一份数据,既能低延迟更新,也能高吞吐扫描。

这件事之所以难,根本原因在于 OLTP 与 OLAP 对存储布局、索引结构、并发控制的要求几乎相反。OLTP 偏好行存、B+ 树索引、MVCC 加行锁、写放大低;OLAP 偏好列存、字典与游程压缩、向量化扫描、批量读取。把两套系统放在一个引擎里,任何一边都要妥协。过去十年,学术界与工业界给出的答案可以粗略归为三类:行列双引擎(TiDB + TiFlash、F1 Lightning)、行列一体存储(SingleStore Universal Storage、SAP HANA、OceanBase),以及”把 OLTP 留在外面,让 OLAP 存储直接承担可更新性”的 Lakehouse 路线(Delta Lake、Iceberg、Hudi)。

本文上半部分梳理这四条路线的论文与核心机制:它们如何做工作负载隔离(Workload Isolation),如何定义分析视图对事务视图的新鲜度(Freshness)边界,以及在一致性/可用性/性能三角里各自站在哪里。下半部分讨论怎么测一个 HTAP 系统——HATtrick 与 CH-benCHmark 的假设是什么、有什么盲点——以及在”我到底需不需要 HTAP”这个问题上的选型决策。

版本说明 本文引用的版本:TiDB 7.x / TiFlash 7.x,SingleStore 8.x,Databricks Delta Lake 3.x,Apache Iceberg 1.4.x,Apache Hudi 0.14.x。涉及版本差异处会单独标注。


一、HTAP 的问题定义与工作负载隔离

1.1 为什么”一份数据两套负载”这么难

数据库里”一份数据”的含义至少有两层:逻辑层面,应用看到的是同一张表;物理层面,实际可能有多份拷贝,分布在不同的节点、不同的存储格式上。HTAP 系统的工程取舍,本质上就是在这两层之间做映射。

OLTP 工作负载的典型特征:小事务、写频繁、点查和短范围扫描为主、延迟要求在毫秒级、并发度几千到几万。行存(Row Store)在这种场景下最划算——一行记录的所有列在物理上相邻,读一行就是一次 I/O;B+ 树索引按主键排列,点查是 O(log n) 的路径。MVCC(Multi-Version Concurrency Control)给每次写入生成新版本,读写互不阻塞,但也意味着同一条记录可能有多个版本共存。

OLAP 工作负载的典型特征:大表全扫、少量列投影、聚合与窗口函数、latency 容忍秒级到分钟级、并发度几十到几百。列存(Column Store)把同一列的值聚在一起,单列扫描的 I/O 放大很低,字典编码、游程编码、位图索引能把存储体积压到行存的 1/5 到 1/20(参见本仓库《列式存储原理》《压缩算法工程实践》)。向量化执行(Vectorized Execution)每次处理一批值,CPU 流水线与 SIMD 指令效率高。

把这两种负载同时压到一个存储引擎上,常见的坏结果有三种:

  1. 缓存互相污染。分析查询一次扫过去几百 GB,把 OLTP 需要的热点页全部挤出 buffer pool,点查延迟开始抖动。
  2. 锁与 MVCC 版本链爆炸。一个长跑的分析事务会让 undo 链无法回收,行存表变得越来越胖,点查也被拖累。
  3. 压缩与索引的两难。OLTP 需要行内更新代价低,不能过度编码;OLAP 需要字典编码和游程编码,每次更新都要重写整个列块。

所有 HTAP 系统都要回答的第一个问题是:工作负载隔离(Workload Isolation)做到哪一层? 是共享同一份数据但在 CPU/内存/IO 层面分流(软隔离),还是物理上复制出第二份面向 OLAP 的副本(硬隔离),或者介于两者之间。

1.2 新鲜度边界:从 “秒级” 到 “读己之写”

HTAP 的第二个核心概念是新鲜度(Freshness):分析侧能看到多新的事务数据?定义这一点需要两个参数:

常见的三档:

档位 可见时延 一致性 典型实现
CDC 型 秒级到分钟级 最终一致 F1 Lightning、传统数仓管线
准实时 亚秒级 快照一致(基于 Raft/Paxos 日志同步) TiDB + TiFlash
完全实时 事务提交即可见 强一致(同一存储,同一事务) SingleStore Universal Storage、HANA

档位越靠下,工程复杂度越高,因为要在写入路径上同步维护行列两份表示。档位越靠上,复杂度越低但业务上要接受”分析结果永远比交易滞后若干秒”。

HTAP 系统的论文和文档里”实时”一词的含义经常不一致。在 TiFlash 的语境下,“实时”是指通过 Raft Learner 同步日志到列存副本、并以事务快照读保证一致性;在 SingleStore 的语境下,“实时”是指同一个存储引擎同时承担行列表示、事务提交即对分析可见。读论文时第一件事是先定位新鲜度档位,再看机制。

1.3 评估 HTAP 系统的坐标系

综合前两小节,可以提出一个四维坐标系来评价任何 HTAP 系统:

  1. 隔离方式:物理副本 / 同一存储双格式 / 完全共享;
  2. 新鲜度档位:CDC / 准实时 / 完全实时;
  3. 事务一致性:快照一致 / 可串行化 / 外部一致(Spanner 式);
  4. 弹性扩展:行列两侧能否独立扩缩容。

本文后续章节会用这四个坐标刻画每个具体系统。

1.4 一个容易忽略的维度:schema 变更

讨论 HTAP 时容易只看”读写分流”,忽略 schema 变更。真实系统里,加列、改类型、加索引每天都在发生,行列双维护对这类操作极不友好:

TiFlash、SingleStore、HANA 都为 online DDL 做了专门设计,但边角场景(改主键、改分区键、改字符集)仍然是踩坑重灾区。评估 HTAP 系统时,把 DDL 支持度单列一个维度,可以识别大量营销话术掩盖下的工程短板。


二、TiDB + TiFlash:基于 Raft Learner 的行列分流

2.1 架构

TiDB 的 HTAP 能力来自 TiFlash 列存副本。Huang 等人在 VLDB 2020 的论文 “TiDB: A Raft-based HTAP Database” 里把它的关键点讲清楚了:

这个架构的关键之处在于:复用 Raft 作为同步通道。Raft 日志天然带全序与 commit index,TiFlash 拉取日志的同时就拿到了一致性读的锚点——只要读取点 Ts 小于 TiFlash 已经 apply 的日志位点所对应的事务提交时间,该读取就是一致的。

2.2 一致性读与 safe-ts

TiDB 的事务采用 Percolator 风格(参见《Percolator 与分布式事务》),提交时从 PD(Placement Driver)获取全局时间戳(TSO)。TiFlash 的快照读有一个 “safe-ts” 概念:对于某个 Region,safe-ts 是一个时间戳 T,保证所有 commit_ts ≤ T 的事务已经在该副本上 apply 完毕。

查询到达 TiFlash 时,如果读取时间戳 Ts ≤ safe-ts,直接读;否则等待 safe-ts 推进到 Ts 或者回退到 TiKV。工程上 safe-ts 由两部分构成:Raft apply 位点对应的最大 commit_ts,以及对未决事务(有 prewrite 但没 commit 的)做 resolve。这一机制让列存侧在不走两阶段提交、不修改 TiKV 写入路径的前提下拿到了快照一致性。

2.3 代价模型与路由

TiFlash 不是总是更快。单行点查、按主键的小范围扫描,TiKV 比 TiFlash 快一个数量级(列存要拼列、要解压)。因此 TiDB 优化器需要一个代价模型来选择副本。

论文里给的简化模型(文字版):

实际工程里还要考虑并发度、索引覆盖情况、是否能下推聚合到 TiFlash(TiFlash MPP 模式允许多机并行聚合再汇总)。优化器选错时可以用 hint(如 /*+ READ_FROM_STORAGE(TIFLASH[t]) */)强制。

2.4 MPP 下推与向量化

TiFlash 从 6.x 开始引入 MPP(Massively Parallel Processing)模式:分析查询可以在多个 TiFlash 节点之间做 shuffle、hash join、聚合下推。对比传统的”单节点扫完拼起来”,MPP 能让大 join / 大 group by 线性扩展。

实际调度里,优化器会根据统计信息估计”这个查询值不值得走 MPP”:

TiFlash 的向量化执行引擎在 v7.x 之后基于 ClickHouse 的执行核心进一步重写,列式 batch、SIMD、表达式 JIT 都能带来明显加速。这是 “行列双维护” 路线里容易被忽略的一点:列存副本不只是存得紧,执行层也要彻底换代,否则白送一半收益

2.5 隔离强度与弹性

回到坐标系:

踩坑点:

  1. TiFlash Learner 同步跟不上时,safe-ts 推进变慢,分析查询要么等要么退回 TiKV。大事务(百万行以上)会显著放大这个问题。
  2. DDL 变更要在 TiKV / TiFlash 两侧协调。加列、改类型的语义要在列存 Delta 合并时正确处理。
  3. TiFlash 的 DeltaTree 在 Delta 层积累过多时点查退化,Stable 层 compaction 又会消耗大量 I/O。Compaction 策略需要按业务实际压力调。

三、SingleStore Universal Storage:同一引擎里的行列一体

3.1 从行存 + 列存两张表到一份表

SingleStore(原名 MemSQL)最早的设计是把行存表和列存表分开:内存行存(Rowstore)吃 OLTP,磁盘列存(Columnstore)吃 OLAP,应用自己选。Universal Storage 是他们在 7.0 之后推出的统一存储格式,核心想法是:

这让一张表在同一份存储里同时具备:列扫描的吞吐、点查的延迟、事务更新的 ACID。代价是每次写入要同时维护多个索引和列段元数据,写放大比纯行存高。

3.2 写入路径

写入一条新行,SingleStore Universal Storage 的大致流程:

  1. 写入 WAL;
  2. 写入活跃行段(row segment)——这是一个内存结构,可看作小的 B+ 树或哈希表;
  3. 更新涉及到的二级索引;
  4. 行段积累到阈值后,后台任务把它”冻结”为只读的列段(Sorted Run),应用列式编码与压缩;
  5. 列段持续 compaction,合并小段、重建索引统计。

更新一条已存在的行则更复杂:如果这行还在活跃行段,原地更新;如果已经进入冻结的列段,标记删除 + 在行段里插入新版本,后续 compaction 再物化。这种 “追加式更新” 对 OLTP 友好,但要求元数据层能快速判断一行的当前有效版本在哪里。

3.3 坐标系定位

代价:单节点在高并发 OLTP 下,同时承担列段 compaction、索引维护、分析扫描,容易出现资源抢占;大分析查询可以通过资源池(Resource Pool)隔离,但本质上还是共享 CPU 与内存。

Universal Storage 的工程价值在于”简化运维”:用户看到的是一张普通 SQL 表,不需要关心行列副本、不需要配同步链路。代价是规模上限与超大分析查询的隔离不如 TiDB 硬副本方案。


四、F1 Lightning:CDC-to-Columnar 的另一种解法

4.1 为什么 Google 不直接改 F1

Yang 等人在 VLDB 2020 的论文 “F1 Lightning: HTAP as a Service” 给出了 Google 内部的 HTAP 方案。背景是 F1 已经是一个跑在 Spanner 上的大规模事务系统,改 F1 和 Spanner 的存储层代价巨大。于是他们选择了”外挂”路线:

这本质上是把 CDC 管线做成”原生服务”,但在几个关键点上比普通 CDC 强:

  1. changepump 保证事件按事务边界、按分区顺序投递,不会出现部分事务可见;
  2. Lightning 维护每个表的安全时间戳(safe timestamp),查询时以此判断数据是否足够新;
  3. Lightning 的列存格式允许合并删除与更新,而不是像传统 CDC + Parquet 那样只能追加。

4.2 新鲜度与隔离

F1 Lightning 的启示是:当底层事务存储无法改动时,HTAP 可以以”官方 CDC + 列存 service”的形式解耦存在。代价是新鲜度比 TiFlash 的 Raft Learner 路线差一档——它不是直接从共识日志上拉,而是从 Spanner 的变更流拉,中间多一跳 changepump。

4.3 和 TiFlash 的对比

F1 Lightning 与 TiFlash 表面都是 “行存 + 列存副本”,但同步层不同:

维度 TiFlash F1 Lightning
同步通道 Raft Learner 日志 changepump(基于 Spanner 变更流)
新鲜度 亚秒级 秒级
与事务层耦合度 紧(同一 Region 的 Raft group) 松(独立服务)
上线成本 需要部署 TiFlash 节点 需要 Lightning + changepump
适配存量系统 仅 TiDB 可扩展到多个前端事务系统

两者的工程哲学差别:TiDB 是”一个项目全栈自研 HTAP”,F1 Lightning 是”给已有事务系统加 HTAP 插件”。


五、Lakehouse:当 OLAP 存储自己支持更新

5.1 Delta Lake 与 Iceberg 的 HTAP 含义

Armbrust 等人在 CIDR 2021 的论文 “Lakehouse: A New Generation of Open Platforms that Unify Data Warehousing and Advanced Analytics” 提出 Lakehouse 概念:在对象存储上直接构建具备 ACID、Schema 演化、时间旅行能力的表格式,代表是 Delta Lake、Apache Iceberg、Apache Hudi。

Lakehouse 与传统 HTAP 的根本差异:它不要求 OLTP 和 OLAP 共享同一份运行时存储,而是接受 OLTP 仍然在外部事务系统里跑,只让 OLAP 存储具备”可更新、可删除、可回溯”的能力,再通过 CDC 入湖或直接写入使”分析侧的存储”也能承载轻量事务写入。

核心机制:

5.2 Lakehouse 不是 HTAP 的全部替代

Lakehouse 的”事务”语义比 OLTP 弱:

所以 Lakehouse 不是”把 MySQL 替掉”,而是”把下游 OLAP 副本从’不可更新的 Parquet 集’升级为’可事务更新的 Parquet 集’“。它和前面三种 HTAP 方案的关系是互补的:OLTP 继续由 MySQL / PostgreSQL / TiDB 承担,Lakehouse 承担”近实时分析 + 机器学习特征 + 历史回溯”。

5.3 坐标系定位

5.4 行列双维护下的 freshness 边界

把这几类 HTAP 放在同一个 freshness 坐标轴上,可以画出一条粗略的谱系:

  freshness
  (越往右越新)
  |
  |  Lakehouse (CDC 入湖)
  |--------------- 分钟级
  |         F1 Lightning
  |--------------- 秒级
  |                   TiDB + TiFlash
  |--------------- 亚秒级
  |                         SingleStore Universal
  |--------------- 事务提交即可见
  |
  v 隔离强度(上强下弱)

越往上,OLTP / OLAP 物理隔离越彻底;越往下,隔离越弱但新鲜度越高。这条谱系不是”越新越好”:对报表场景,秒级已经够用;对实时风控、库存一致性场景,才必须下沉到”事务可见即可读”。


六、如何测 HTAP 系统:CH-benCHmark 与 HATtrick

6.1 CH-benCHmark

Cole 等人在 2011 年提出的 CH-benCHmark 是 HTAP 领域最早、也是最流行的混合基准。它把 TPC-C(事务)和 TPC-H(分析)拼到同一张数据集上:TPC-C 负责持续生成事务,TPC-H 的 22 个分析查询在同一份数据上并发运行。

CH-benCHmark 关心两组指标:

关键点在于”并发”:OLAP 查询必须和 OLTP 同时跑,观察两侧是否会互相干扰。一个 HTAP 系统即便 OLTP 单独跑很快、OLAP 单独跑也很快,只要并发时互相拖慢超过 20–30%,就说明工作负载隔离做得不好。

6.2 HATtrick

HATtrick(Sirin 等人,ICDE 2021 附近的系列工作)在 CH-benCHmark 基础上改进了两点:

  1. 可调的分析/事务比例:允许用参数控制分析查询的并发度,扫掉 CH-benCHmark 固定为 “1 个 OLAP 客户端 + N 个 OLTP 客户端” 的局限;
  2. 新鲜度测量:明确给出”事务提交到分析查询可见”的时延分布,把上一节的”freshness 坐标”从概念变成可测数字。

HATtrick 的流程大致是:

  1. 事务端定期插入带时间戳的”探针行”;
  2. 分析端定期扫表计算”最大探针时间戳”;
  3. 两者的差值即新鲜度 lag。

这一指标几乎比任何”平均查询延迟”都更能反映 HTAP 系统的真实能力。

6.3 基准测试的常见盲点

照搬论文基准时要小心:

  1. 数据集规模不代表真实压力。TPC-C 的 WH=100 只有约 10 GB 数据,TiFlash 在这个规模下 compaction 几乎不工作,实际生产规模(数百 GB 到 TB)表现完全不同。
  2. TPC-H 的 22 个查询偏向静态分析,不覆盖交互式 BI 的”很多轻查询”场景。对交互式 BI,更合适的是自建 query log replay。
  3. 忽略 DDL 与 schema 变更。真实系统的 HTAP 路径在加列、改类型、加索引时最容易出问题。
  4. 只测稳态,不测扩缩容。列存副本扩容时的数据搬迁、safe-ts 重建代价,在任何标准基准里都没有直接体现。

6.4 自建基准的几个建议

真正要评估 HTAP 系统,建议在 CH-benCHmark 基础上自建一个小工具集,包含:

比起只看一个 tpmC / QphH 数字,这些微基准更能暴露一个 HTAP 系统的真实工程质量。


七、选型决策:我到底需不需要 HTAP

7.1 一个简单的决策树

问题:分析报表对事务数据的新鲜度要求?
 ├── 分钟级及以上    -> 不要 HTAP,上 Lakehouse/数仓 + CDC
 │
 ├── 秒级            -> 优先考虑 F1 Lightning 式的"外挂列存 service";
 │                     小规模可以直接 TiDB + TiFlash
 │
 ├── 亚秒级          -> TiDB + TiFlash、OceanBase;
 │                     或 SingleStore Universal Storage
 │
 └── 事务提交即可见  -> SingleStore Universal Storage、SAP HANA;
                       接受单点扩展上限

另一条维度是隔离强度:

7.2 常见的”假 HTAP 需求”

在咨询和 code review 里,有些 HTAP 需求其实不成立:

  1. “我们要实时报表,用 HTAP 吧。” 先问报表查询的 QPS 和数据量。如果只是一个 dashboard,每分钟刷一次、扫几百万行,直接在 TiDB/MySQL 上建物化视图或上 Lakehouse 就够了,不必引入列存副本。
  2. “我们 OLTP 很慢,加个列存加速一下。” 点查变慢通常是索引/缓存/锁问题,列存加不进来。
  3. “我们分析查询很慢,加个 HTAP 副本。” 如果分析查询是”从 1 亿行里找 10 行”,这是索引问题不是列存问题;只有扫描量占表的很大比例时列存才明显更快。

判断是否真需要 HTAP 的一个简单启发:把 OLAP 查询的扫描行数 / 查询结果行数 算一下,如果比值 ≥ 1000,列存大概率有收益;如果 ≤ 10,加索引就够了。

7.3 与本仓库其它文章的互链

7.4 迁移与回滚

HTAP 不是一次性决策,而是一段演进过程。常见迁移路径:

  1. 纯 OLTP + 外部数仓(起点):MySQL/PostgreSQL 做事务,夜间 ETL 到 Hive/ClickHouse。
  2. CDC 实时入湖:Debezium / Maxwell 把 binlog 实时推入 Kafka + Lakehouse,分析侧从分钟级降到秒级。
  3. 引入列存副本:对核心业务表加 TiFlash 副本或用 SingleStore Universal Storage,分析侧 join 事务表变快。
  4. 统一查询入口:通过查询代理(如 F1 Query、Presto / Trino 连到多数据源)让应用不感知底层存储差异。

回滚也要预留:每一步都要可以独立关掉。尤其第 3 步,列存副本出问题时要能快速切回”只读 OLTP” 兜底,不能全线崩。很多踩坑的团队就是因为一开始没想好回滚路径,到问题爆发时只能硬扛。


八、demo 与工程实验建议

本目录下 demo/ 放一个最小的 CH-benCHmark 运行脚本(基于 benchbasego-tpc),用 Docker Compose 启动一个 TiDB + TiFlash 单机集群,跑 WH=10 的 TPC-C 同时并发一个 TPC-H Q6。观察:

这不是标准 HATtrick,但对”理解一次真实的 HTAP 调度”足够。docker-compose.yml 与运行说明见 demo/README.md


九、小结

HTAP 不是单一方案,而是一组围绕”一份数据 × 两类负载”的工程取舍:

  1. TiDB + TiFlash:Raft Learner 路线,物理副本、亚秒级新鲜度、行列独立扩展,适合”事务写入由 TiDB 承担、同时要近实时分析”的场景。
  2. SingleStore Universal Storage:一体化路线,行列共存、事务提交即可见,适合中等规模、需要极低 lag 的 HTAP 工作负载。
  3. F1 Lightning:外挂路线,CDC + 列存 service,适合改不动底层事务系统但又要统一查询入口的场景。
  4. Lakehouse:分离路线,OLTP 在外、OLAP 存储自身具备事务语义,适合”分析才是主战场、事务交给别人”的平台型产品。

读论文与做选型时,先用第一节给出的四维坐标(隔离 / 新鲜度 / 一致性 / 弹性)定位,再按第七节的决策树做取舍,能避免大部分”选了 HTAP 又后悔”的情况。

最后提醒一点:HTAP 是 2015 年之后才开始被严肃工程化的话题。很多结论仍然在演变,本文归纳的四种模式也不一定能覆盖 5–10 年后的全部情况。与其记住具体论文,不如记住分析方法:任何一个自称”HTAP” 的系统,都可以用”它在新鲜度坐标的哪一档、它的工作负载隔离做到哪一层、它怎么测”这三个问题拆开看。


参考文献

  1. Huang D., et al. TiDB: A Raft-based HTAP Database. VLDB 2020. https://www.vldb.org/pvldb/vol13/p3072-huang.pdf
  2. Yang J., et al. F1 Lightning: HTAP as a Service. VLDB 2020. https://www.vldb.org/pvldb/vol13/p3313-yang.pdf
  3. Armbrust M., et al. Lakehouse: A New Generation of Open Platforms that Unify Data Warehousing and Advanced Analytics. CIDR 2021. https://www.cidrdb.org/cidr2021/papers/cidr2021_paper17.pdf
  4. Armbrust M., et al. Delta Lake: High-Performance ACID Table Storage over Cloud Object Stores. VLDB 2020. https://www.vldb.org/pvldb/vol13/p3411-armbrust.pdf
  5. Cole R., et al. The Mixed Workload CH-benCHmark. DBTest 2011.
  6. Sirin U., et al. A Methodology for Performance Analysis of Mixed OLTP/OLAP Workloads. (HATtrick 相关工作)
  7. SingleStore Documentation. Universal Storage. https://docs.singlestore.com/
  8. Corbett J. C., et al. Spanner: Google’s Globally-Distributed Database. OSDI 2012.

上一篇【数据库研究前沿】多模态数据库

下一篇【数据库研究前沿】Serverless 数据库弹性理论:Neon 与 Aurora Serverless v2

同主题继续阅读

把当前热点继续串成多页阅读,而不是停在单篇消费。


By .