传统数据库架构将在线事务处理(OLTP)和分析处理(OLAP)分离:核心业务数据存储于 MySQL/PostgreSQL 等行存数据库,批量导入数据仓库进行报表分析。这种架构存在数据延迟、架构复杂等问题。HTAP(Hybrid Transactional/Analytical Processing)旨在用一套系统同时高效处理两种负载。本文从存储引擎原理到工程实践全面解析 HTAP。
1. OLTP vs OLAP 的本质差异
| 维度 | OLTP | OLAP |
|---|---|---|
| 数据操作 | 单行/小范围增删改查 | 大批量聚合分析 |
| 查询特征 | 点查、短事务 | 全表扫描、复杂 JOIN |
| 数据状态 | 当前最新数据 | 历史快照 |
| 响应时间 | < 100ms | 秒级 ~ 分钟级 |
| 并发量 | 数千 ~ 数万 QPS | 数十并发 |
| 存储模型 | 行存储 | 列存储 |
| 典型系统 | MySQL、PostgreSQL | ClickHouse、Snowflake |
2. 行存储 vs 列存储
2.1 行存储(Row-Oriented)
磁盘布局(按行组织):
Page 0: [id=1, name=Alice, age=30, city=BJ] [id=2, name=Bob, age=25, city=SH] ...
Page 1: [id=1001, name=...] ...
适合:SELECT * FROM users WHERE id = 1 (一行全部列在一起)
不适:SELECT AVG(age) FROM users (需要跳过 name/city,读放大)
2.2 列存储(Column-Oriented)
磁盘布局(按列组织):
id 文件: [1, 2, 3, 4, 5, ...]
name 文件: [Alice, Bob, Carol, ...]
age 文件: [30, 25, 35, 28, ...]
city 文件: [BJ, SH, GZ, SZ, ...]
适合:SELECT AVG(age) FROM users (只读 age 列,顺序 IO)
不适:SELECT * FROM users WHERE id = 1 (需读取多个列文件合并)
2.3 列存储的优化技术
| 技术 | 原理 | 收益 |
|---|---|---|
| 数据压缩 | 同类型数据连续,压缩比高 | 存储量减少 70%~90% |
| 向量化执行 | SIMD 指令一次处理一批数据 | CPU 利用率提升 |
| ** Zone Maps** | 每列块记录 min/max | 快速跳过不满足条件的块 |
| 延迟物化 | 先过滤再组装完整行 | 减少不必要的数据读取 |
3. 传统分离架构
3.1 ETL 数据管道
┌──────────┐ ETL(定时) ┌──────────┐ SQL ┌─────────┐
│ 业务库 │ ──► 抽取/转换/加载 ──► │ 数据仓库 │ ──►│ BI报表 │
│ MySQL/ │ (T+1 延迟) │ Hive/ │ │ Grafana │
│ PostgreSQL│ │ ClickHouse│ │ Tableau │
└──────────┘ └──────────┘ └─────────┘
痛点:
- 数据延迟 T+1,无法实时分析
- ETL 管道复杂,维护成本高
- 两套存储,数据冗余
- MySQL 大查询拖垮 OLTP
3.2 实时同步方案
-- Canal/Debezium 实时 CDC 同步
MySQL Binlog ──► Kafka ──► Flink/Spark ──► ClickHouse
延迟降低至秒级,但架构更复杂。
4. HTAP 架构方案
4.1 方案一:单系统行/列双存储(TiDB TiFlash)
TiDB Server ──► TiKV (行存, OLTP) ◄── 实时复制 ──► TiFlash (列存, OLAP)
(Raft 协议) (Raft Learner)
查询路由器根据 SQL 特征自动选择存储:
- SELECT id, name WHERE id = 100 → TiKV(行存)
- SELECT COUNT(*), AVG(price) GROUP BY category → TiFlash(列存)
4.2 方案二:内存列存加速(Oracle In-Memory)
磁盘:行存储(Buffer Cache)
内存:列存储(In-Memory Column Store)
事务先写入行存储,后台将变更同步到内存列存。
分析查询走内存列存,实现近实时分析。
4.3 方案三:计算存储分离(Snowflake、Databricks)
存储层:S3 对象存储(列存文件)
计算层:弹性 warehouses(按需启动)
数据以列存格式持久化在 S3,查询时拉取所需列到本地缓存。
完全分离 OLTP(外部 CDC 导入)和 OLAP。
4.4 方案对比
| 方案 | 代表产品 | OLTP 延迟 | OLAP 延迟 | 架构复杂度 |
|---|---|---|---|---|
| 行+列双引擎 | TiDB + TiFlash | < 10ms | < 100ms | 中 |
| 内存列存 | Oracle IM | < 10ms | < 50ms | 低 |
| 计算存储分离 | Snowflake | N/A(外部导入) | < 1s | 中 |
| ETL 管道 | Hive/传统数仓 | N/A | T+1 ~ 小时 | 高 |
5. ClickHouse 实时分析实战
5.1 核心特性
-- 建表(MergeTree 引擎,列存储)
CREATE TABLE events (
event_date Date,
user_id UInt64,
event_type LowCardinality(String),
properties String CODEC(ZSTD(1))
) ENGINE = MergeTree()
PARTITION BY toYYYYMM(event_date)
ORDER BY (event_type, event_date, user_id);
-- 向量化聚合查询
SELECT
event_type,
count() AS total,
uniqExact(user_id) AS unique_users,
avg(length(properties)) AS avg_props_len
FROM events
WHERE event_date >= today() - 7
GROUP BY event_type
ORDER BY total DESC;
5.2 实时同步架构
MySQL ──► Canal/Debezium ──► Kafka ──► ClickHouse Kafka Engine
ClickHouse Kafka Engine 直接消费 Kafka:
CREATE TABLE events_queue (
user_id UInt64,
event_type String,
ts DateTime
) ENGINE = Kafka()
SETTINGS kafka_broker_list = 'kafka:9092',
kafka_topic_list = 'events',
kafka_group_name = 'ch_consumer',
kafka_format = 'JSONEachRow';
-- Materialized View 自动将 Kafka 数据写入 MergeTree
CREATE MATERIALIZED VIEW events_mv TO events AS
SELECT * FROM events_queue;
5.3 ClickHouse 性能优化
| 优化点 | 说明 |
|---|---|
| 分区裁剪 | PARTITION BY 让查询只读目标分区 |
| 主键排序 | ORDER BY 列保证数据有序,快速过滤 |
| 列裁剪 | 只读取所需列 |
| 向量化 | SIMD 加速批量数据处理 |
| 物化视图 | 预聚合常用查询 |
| 数据类型 | 使用 Int 替代 String、LowCardinality 压缩 |
6. TiDB HTAP 实战
6.1 TiFlash 架构
SQL 解析 + 优化器
│
┌──────────────────────┼──────────────────────┐
│ │ │
▼ ▼ ▼
TiKV Region TiKV Region TiFlash Segment
(行存 Leader) (行存 Follower) (列存 Learner)
Raft Log 复制:Leader(行存) ──► Follower(行存) ──► Learner(列存, TiFlash)
优化器根据代价模型选择:
- 点查、短事务 → 行存 TiKV
- 分析查询、聚合 → 列存 TiFlash
6.2 智能路由
-- TiDB 自动选择,无需关心底层
SELECT * FROM orders WHERE order_id = 100; -- 路由到 TiKV
SELECT region, COUNT(*), AVG(amount)
FROM orders
GROUP BY region; -- 优化器自动路由到 TiFlash
-- 强制指定(不推荐)
SELECT /*+ read_from_storage(tiflash[orders]) */ * FROM orders;
6.3 MPP 查询加速
TiFlash 支持 MPP(Massively Parallel Processing):
SELECT region, COUNT(*), SUM(amount)
FROM orders o JOIN customers c ON o.customer_id = c.id
GROUP BY region;
执行计划:
- 将 JOIN 和聚合下推到多个 TiFlash 节点并行执行
- 节点间 Shuffle 数据
- 局部聚合后汇总
- 比单节点分析快 10~100 倍
7. HTAP 设计要点
7.1 数据一致性
| 级别 | 延迟 | 实现方式 |
|---|---|---|
| 强一致 | 0 | TiFlash Raft Learner 同步(等待 apply) |
| 最终一致 | 毫秒级 | Raft Learner 异步 apply |
| 近实时 | 秒级 | Canal + Kafka + ClickHouse |
| 准实时 | 分钟级 | 定时 ETL Batch |
7.2 资源隔离
单机部署 HTAP 的核心挑战:分析查询占用大量 CPU/IO,影响事务。
解决方案:
1. 物理隔离:OLTP 和 OLAP 跑在不同节点(TiKV/TiFlash 分离)
2. 资源组限制:CPU/内存配额(TiDB Resource Control)
3. 优先级调度:OLTP 查询优先级高于 OLAP
4. 读写分离:OLAP 路由到只读副本
8. 选型建议
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| MySQL 已有业务 + 实时报表 | TiDB + TiFlash | 无缝迁移,智能路由 |
| 纯分析、日志/事件分析 | ClickHouse | 极致列存性能 |
| 复杂数据仓库、多数据源 | Snowflake/Databricks | 弹性计算,生态成熟 |
| Oracle 存量 + 分析加速 | Oracle In-Memory | 无需改架构 |
| 中小规模、预算有限 | PostgreSQL + 物化视图 + 读写分离 | 够用就好 |
9. 总结
HTAP 代表了数据库架构的演进方向:
传统架构: HTAP 架构:
OLTP ──► ETL ──► OLAP OLTP ═══ 实时复制 ═══ OLAP
│ │ │ │ │
▼ ▼ ▼ ▼ ▼
MySQL 小时延迟 Hive TiDB ─── TiFlash ClickHouse
│ (统一系统) (列存消费)
T+1 毫秒延迟 秒级延迟
HTAP 的核心价值不是完全消除延迟,而是在可接受的架构复杂度下,让分析负载不再干扰事务处理,同时大幅降低数据管道的维护成本。随着 TiDB、ClickHouse 等系统的成熟,HTAP 正从概念走向大规模生产实践。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。