TiDB 引擎选路排查指南:TiKV vs TiFlash 适用场景与性能调优
在 TiDB 的 HTAP(混合事务与分析处理)架构中,系统同时具备行存引擎 TiKV 与列存引擎 TiFlash。优化器(CBO)在接收到 SQL 后,会自动评估代价并决定是将计算下推给 TiKV 还是 TiFlash。
但在实际生产运维中,经常会遇到“为什么这个聚合查询没有走 TiFlash?”、“为什么明明加了 TiFlash 副本,查询反而变慢甚至 OOM?”等问题。理解两者的底层存储与计算模型差异,掌握正确的选路基准与调优方法,是保障 TiDB 集群高效运行的关键。
本文将从底层架构原理、选路决策基准、典型避坑案例及执行计划调优四个方面,全面解析 TiKV 与 TiFlash 的选路逻辑。
一、核心决策速查表
在分析具体 SQL 之前,可以通过以下几个关键维度快速判断查询应该走哪个存储引擎:
| 判定维度 | 走 TiKV(行存) | 走 TiFlash(列存 + MPP) |
|---|---|---|
| 典型业务场景 | OLTP 事务、精准点查、高频业务 API | OLAP 报表、BI 看板分析、海量数据重度聚合 |
| 扫描数据行数 | $< 10^4$ 行(通过索引快速收敛) | $> 10^5 \sim 10^9$ 行(大范围扫描或全表扫描) |
| 访问列占总列数比例 | $> 50%$ 或 SELECT * 宽表读取 | $< 20%$(尤其是单列/少列的聚合计算) |
| 索引可用性 | 存在高区分度的二级索引或覆盖索引 | 无可用索引、低选择性过滤或大跨度范围过滤 |
| Join 计算形式 | 小表 Lookup Join / 驱动表索引关联 | 大表 Hash Join / 跨节点 MPP Shuffle Join |
| 并发与延迟目标 | 高 QPS(上万)、超低延迟($< 10\text{ms}$) | 低 QPS(几十 |
二、底层存储与算力模型差异
TiKV 与 TiFlash 不仅仅是“行存”与“列存”的区别,其背后的数据组织方式和分布式计算能力有着根本的不同:
+-------------------------------------------------------------+
| TiDB Server (CBO) |
+------------------------------+------------------------------+
|
+------------------+------------------+
| |
v v
+-----------------------+ +-----------------------+
| TiKV Cluster | | TiFlash Cluster |
+-----------------------+ +-----------------------+
| • RocksDB LSM-Tree | | • DeltaTree 列存 |
| • 行式物理存储 | | • 按列压缩与独立切块 |
| • Coprocessor 算子 | | • MPP 节点间 Shuffle |
| • B-Tree 索引二分查找| | • SIMD 向量化加速 |
+-----------------------+ +-----------------------+
1. TiKV(行存 + 索引寻道)
- 存储模型:基于 RocksDB 的 LSM-Tree 行式存储,单行数据的所有字段在物理上紧凑连续存储。
- 计算机制:利用 Coprocessor 将
Filter、TopN及简单聚合算子下推到单个 Region 执行。 - 核心优势:通过主键或二级索引做点查或小范围 Seek 时,几乎无离散列组装开销;配合覆盖索引(Covering Index),可实现微秒至毫秒级的极速响应。
2. TiFlash(列存 + MPP 分布式计算)
- 存储模型:基于针对分析场景自研的 DeltaTree 列式存储引擎,每列数据单独存储并进行高比例压缩。
- 计算机制:
- 列投影过滤(Column Projection):查询几列就只从磁盘读取几列,极大降低 I/O 吞吐开销。
- MPP 架构:支持在 TiFlash 存储节点之间直接通过网络进行 Partition/Broadcast 数据 Shuffle 与分布式 Hash Join,无需将海量中间结果汇聚到 TiDB Server 单点,打破了单机内存与 CPU 瓶颈。
- 向量化与 Rough Set:利用 CPU SIMD 指令批量计算,并借助数据块的 Min-Max 粗糙集索引快速跳过无关数据段。
三、场景判定与特征分析
1. 优先走 TiKV 的典型场景
- 主键、唯一键或高区分度索引点查
- 特征:
WHERE id = 1001或WHERE uid = 456 AND status = 1。 - 原因:B-Tree 索引二分查找极快;TiFlash 没有行级二级索引,点查只能做全表扫描,代价极高。
- 特征:
- 高频数据写入、更新与删除
- 特征:业务事务中的
INSERT、UPDATE ... WHERE id = ...。 - 原因:TiFlash 是只读的 Raft Learner 副本,事务写入与行锁由 TiKV 的 Raft Leader 协调处理。
- 特征:业务事务中的
- 宽表全字段投影(
SELECT *)- 特征:表包含数十上百列,且业务需要返回该行的所有字段。
- 原因:列存读取全部列会引发“列拼接(Column Reconstruction)”,产生大量离散 I/O 与内存组装开销,整体性能反而弱于行存。
- 覆盖索引查询(Covering Index Scan)
- 特征:SQL 所需的所有列(包括 Filter 和 Select)均包含在联合索引中。
- 原因:TiKV 直接在索引元数据上完成扫描与聚合,完全省去了回表(IndexLookUp)的成本。
2. 优先走 TiFlash 的典型场景
- 大宽表统计少量列(Agg & Group By)
- 特征:
SELECT dept_id, SUM(amount), AVG(duration) FROM sales GROUP BY dept_id。 - 原因:列存只扫描计算涉及的三列数据,I/O 消耗仅为行存全行扫描的百分之一甚至更低。
- 特征:
- 缺乏高选择性索引的大范围扫描
- 特征:
WHERE create_time BETWEEN '2025-01-01' AND '2025-12-31'(涉及数千万行甚至数亿行)。 - 原因:此时走 TiKV 索引回表会导致海量随机 I/O,走 TiKV 全表扫则数据量过大;TiFlash 的列存高吞吐扫描与 Min-Max 过滤优势显著。
- 特征:
- 海量数据跨表关联(MPP Join)
- 特征:千万级订单事实表与百万级商户/商品维度表关联。
- 原因:TiFlash MPP 引擎在存储节点间并行完成 Hash Join 计算,避免了将几千万行中间数据回传至 TiDB Server 造成的网络与内存爆满。
- 低 QPS 的重度分析与报表接口
- 特征:后台运营看板、财务月结统计,对 QPS 要求不高,但要求单次查询在亚秒或数秒内完成。
四、常见排查误区与避坑案例
踩坑 1:看到聚合慢查询就盲目加 Hint 强制走 TiFlash
- 典型误区:认为只要 SQL 中有
COUNT(*)、SUM()且耗时长,就应该走 TiFlash。 - 真实案例:某现货订单表根据单一用户 ID 统计近 30 天未完成订单:原有索引为
SELECT COUNT(*) FROM orders WHERE uid = 880192 AND ctime > 1700000000 AND status IN (1, 2);(uid, status, ctime),由于范围条件ctime在最后,导致前缀 Seek 无法截断,TiKV 扫描了该 UID 下 100 多万条历史索引,耗时 600ms。 如果强行加 Hint 走 TiFlash,由于 TiFlash 没有uid的二级索引,会退化为大范围扫描甚至冷数据远程拉取,耗时反而增加到数秒。 - 正确解法:单 UID/单租户的聚合点查,本质仍属于 OLTP 范畴,应调优 TiKV 联合索引列顺序 为
(uid, ctime, status),让前缀条件直接收敛扫描行数至百行以内,耗时降至 2ms。
踩坑 2:在 TiFlash 上执行 SELECT * 导致 OOM
- 典型误区:误以为只要机器资源够,TiFlash 的速度任何时候都比 TiKV 快。
- 后果:对 60+ 列的大宽表执行
SELECT *走 TiFlash,TiFlash 需要将每一列分别解压并拼装成行,引发巨大的内存占用和 CPU 序列化消耗,极易导致 TiFlash 节点或 TiDB Server OOM。 - 规范:面向 TiFlash 的分析查询,必须严格遵守 按需投影(Column Pruning) 原则,绝不允许滥用
SELECT *。
踩坑 3:统计信息过期导致优化器选路失真
- 现象:业务逻辑未变,原本走 TiKV 毫秒级返回的 SQL 突然变慢并转走了 TiFlash 全表扫,或者本来该走 TiFlash 的大分析被误派给 TiKV 导致慢查询。
- 排查方法:
-- 检查表的统计信息健康度 SHOW STATS_HEALTHY WHERE Table_name = 'orders'; -- 查看 EXPLAIN 中的预估行数(estRows)与实际扫描行数(actRows)是否偏差过大 EXPLAIN ANALYZE SELECT ...; - 解决办法:重新收集统计信息并设置合理的自动收集调度:
ANALYZE TABLE orders;
五、执行计划识别与人工控制
1. 执行计划(EXPLAIN)关键特征
通过 EXPLAIN 查看 SQL 执行计划时,重点观察 Task 列与算子名称:
- 走 TiKV 的标志:
- Task 列出现
cop[tikv]或root。 - 算子包含
Point_Get、IndexLookUp、IndexRangeScan、IndexReader等。
- Task 列出现
- 走 TiFlash 的标志:
- Task 列出现
cop[tiflash]或mpp[tiflash]。 - 算子包含
TableFullScan(列存块读取)、ExchangeSender/ExchangeReceiver(MPP 节点间数据分发)。
- Task 列出现
2. 人工控制与 Hint 语法
当优化器由于统计信息波动或复杂表达式导致选路非最优时,可通过 Hint 人工指定存储引擎:
强制指定走 TiFlash:
SELECT /*+ READ_FROM_STORAGE(TIFLASH[t1, t2]) */ dept_id, SUM(amount) FROM sales t1 JOIN department t2 ON t1.dept_id = t2.id GROUP BY dept_id;强制指定走 TiKV:
SELECT /*+ READ_FROM_STORAGE(TIKV[orders]) */ * FROM orders WHERE order_sn = 'SN20260824001';MPP 模式开关控制:
-- 会话级开启 MPP(默认开启) SET @@tidb_allow_mpp = ON; -- 临时关闭 MPP(降级为单节点 Coprocessor 收集后回传 TiDB 计算) SET @@tidb_allow_mpp = OFF;
六、选路决策流程图
+------------------------------+
| SQL 调优 / 引擎选路决策 |
+--------------+---------------+
|
v
/------------------------------\
/ 单次预估扫描行数规模? \
+--------------------------------+
| |
| < 10^4 行 | > 10^5 行
v v
/----------------------\ /----------------------\
/ 是否有高区分度索引? \ / 查询涉及列占比? \
+--------------------------+ +--------------------------+
| | | |
| 是 | 否 | 少量列 / 聚合计算 | 大量列 / SELECT *
v v v v
+------------------+ +-------+ +------------------+ +------------------+
| 走 TiKV | | 尝试建| | 走 TiFlash | | 评估裁剪投影列 |
| 依靠索引极速返回 | | 覆盖索| | 利用列存+MPP加速 | | 或改走行存分页 |
+------------------+ +-------+ +------------------+ +------------------+
掌握了上述核心原理与选路特征后,在面对慢查询报警时,就能从“盲目加 Hint”转变为“从数据规模、索引结构与列投影比例系统性分析”,从而做出最合理的性能调优决策。