想象你经营着一家快速扩张的连锁茶饮品牌「TT茶饮」。每开一家新店,你都会遇到同样的难题:点单员再能干,也架不住后厨原料仓太小、账本太乱。开十家店,就要建十个仓库、配十本账,成本高得吓人。TiDB 的设计者换了个思路:把「点单做茶」(计算)和「囤原料」(存储)彻底分开。全城只建几个中央原料仓(TiKV),所有门店共享;总部设一个调度台(PD),记着每包原料放在哪个仓;再请几个专职分析员(TiFlash),把原料按品类记账,方便老板随时看经营报表。点单员可以随时增聘(水平扩展),仓库也能自由扩建,互不拖累。这套「多点单员 + 中央原料仓 + 分析台账」的架构,就是 TiDB 的分布式内核。本文就用这个茶饮集团的故事,带你拆解 TiDB 为什么既能扛住海量交易,又能秒出分析报表。

岗位关联

对 AI 从业者而言,TiDB 是理解「分布式系统如何保证一致性、如何混合支撑交易与分析」的绝佳范本。学完它,你在做数据底座选型、容量规划和 AI 应用的数据管道设计时,会多一个带「分布式思维」的选项,而不是只会堆 MySQL 或 Redis。

目录
  1. 可视化演示
  2. 整体分层架构:把门店与仓库分开
  3. PD调度中枢:总部手里的「小包分布簿」
  4. TiKV行式引擎:按「整杯茶」存原料
  5. Raft一致性协议:三家仓库对账的规矩
  6. TiFlash列式引擎:总部的分析台账
  7. HTAP混合负载:一套系统,两套账本
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

动态演示深入TiDB分布式架构与存储的核心机制,通过全局/局部/动态/静态四种模式展示关键概念与交互流程。

本节配有交互动效(进入视口后自动加载,约 3KB)。

深入TiDB分布式架构与存储:TiDB TiDB计算存储分离架构与PD TiKV行存引擎与Multi- 核心机制可视化 深入TiDB分布式架构与存储整体架构与关键组件

整体分层架构:把门店与仓库分开

「TT茶饮」扩张到第 50 家店时,你发现真正的瓶颈不是奶茶配方,而是每家店都得囤一大堆原料:珍珠、茶底、果酱、冰块……新店开业光备货就要一周。于是你做了一个大胆决定——砍掉所有门店的后厨,改成「中央原料仓+多点单员」模式:门店只留点单员,原料全部集中到几个大仓库,谁接单谁去仓库取料。TiDB 正是这个思路:把「计算」和「存储」分离,各自独立扩展。

这个思路落到技术上,就是 TiDB 的三层架构:最上层是无状态的 TiDB Server(点单员团队),负责接收业务发来的 SQL 请求、解析语法、生成执行计划;中间是 PD(总调度台),负责记录数据分布在哪些仓库、分配全局时间戳;最下层是 TiKV(中央原料仓),真正保存数据。TiDB Server 不存任何业务数据,因此它「无状态」——服务器崩溃了,重新拉起一个空实例就能继续服务,就像换一批点单员,业务不受影响。

「无状态」带来了一个杀手级优势:可以像搭积木一样水平扩展。业务量翻倍?把 TiDB Server 从 3 台加到 6 台,前端负载均衡器自动分流;存储不够了?给 TiKV 集群加节点,数据自动按 Region 重新分布。这种「计算加人、存储加仓」的灵活扩容方式,是传统分库分表方案难以做到的。

回到奶茶店的比喻:你不需要给每家新店重新建仓库,只要「增聘点单员+共享中央仓库」,开店速度自然甩开同行。TiDB 的存算分离,让计算资源与存储资源像两条独立的流水线,按需扩充、互不拖累。

S(n)≈n×S(1)\displaystyle S(n) \approx n \times S(1)
这个公式表示:当集群从 1 台机器扩展到 n 台机器时,整体处理能力近似乘以 n。当然「近似」意味着存在网络开销和管理损耗,就像新店员的磨合成本一样。
  1. 整体吞吐 $S(n)$$S$ 表示集群能承载的每秒请求量,括号里的 $n$ 是计算节点数量。节点越多,整体吞吐越高,这是水平扩展的核心收益。
    S(n)\displaystyle S(n)
  2. 单机能力 $S(1)$一台 TiDB Server 单独能处理的请求量,代表扩容的「基础单位」。不同的硬件配置对应不同的 $S(1)$。
    S(1)\displaystyle S(1)
  3. 近似线性增长 $\approx n \times S(1)$如果每台机器能力相同且互不干扰,n 台机器的总能力就是 $n$ 倍的 $S(1)$。实际中因为协调开销会略低于这个值,所以用「≈」而不是「=」。
    n×S(1)\displaystyle n \times S(1)

PD调度中枢:总部手里的「小包分布簿」

原料集中到中央仓库后,新问题来了:仓库不只一个,珍珠放在 A 仓,果酱放在 B 仓,而一杯「珍珠奶茶」需要同时取两边的料。点单员不可能记住所有原料的位置,于是总部设了一个调度台——它手里有一本「小包分布簿」,记录着每一包原料(Region)放在哪个仓库、当前由哪个仓库的哪个货架负责。点单员接单后先问调度台,再精准取料。

这个调度台就是 PD(Placement Driver)。它负责三件大事:第一,维护「key→Region→存储节点」的路由表,让每个请求都能快速找到数据;第二,分配全局单调递增的时间戳 TSO,给事务排序用的「时钟」;第三,持续做调度——发现某个仓库太忙,就把部分 Region 迁到空闲仓库;发现某个 Region 的 Leader 副本负载过高,就把 Leader 角色转移。

TiDB 的数据不是一大块,而是按主键范围切成许多小片,每片叫一个 Region(默认最大约 96MB)。每个 Region 的 key 范围是一个左闭右开区间,就像把一本书按页码切成若干小册子,每一小册的页码范围清清楚楚。PD 的路由表就记录着:「第 100~199 页这本小册子,放在 A 仓库」。

有了调度台的「小包分布簿」,任何一台点单机都能在毫秒内问出原料位置。PD 本身也组成一个小集群(多数派机制),即使某台调度机宕机,调度台整体仍然可用——就像总部备了三本一模一样的分布簿,两本还在就能对账。

key∈[start_key, end_key)\displaystyle key \in [start\_key,\ end\_key)
这表示某条数据的主键 key 落在哪个 Region 的范围内。左闭右开意味着下界包含、上界不包含,避免相邻 Region 的边界重叠。
  1. 数据主键 $key$每一行数据都有唯一的标识,类似奶茶订单号。TiDB 按 key 的大小排布数据,查询时通过 key 定位。
    key\displaystyle key
  2. 区间下界 $start\_key$当前 Region 管辖的最小 key 值,用方括号「[」表示包含这个边界,防止漏数据。
    [start_key\displaystyle [start\_key
  3. 区间上界 $end\_key$当前 Region 管辖的最大 key 之后的位置,用圆括号「)」表示不包含这个边界,保证相邻 Region 之间没有空隙也没有重叠。
    end_key)\displaystyle end\_key)

TiKV行式引擎:按「整杯茶」存原料

总部决定,每个中央仓库的货架按「一杯茶的完整配方」来摆放原料:一个货格放的就是「某杯珍珠奶茶所需的全部原料」——茶底、珍珠、奶盖、糖度,整整齐齐放在一起。点单员要制作这杯茶,只需按杯号取出一整格原料,一次拿齐。这就是 TiKV 的「行式存储」:一行数据的所有列(字段)连续存放在一起。

TiKV 本质上是一个分布式的 Key-Value 引擎,按 key 排序存储。底层采用 LSM-Tree(日志结构合并树)结构:新数据先追加写入内存中的有序表(MemTable),积累到一定量再批量刷到磁盘,因此写入是高效的顺序写。它不像传统 B+ 树那样在磁盘上随机修改旧数据,而是「只追加、后合并」,把随机写变成顺序写,这也是它能扛住海量写入的关键。

为了让读写并发不打架,TiKV 引入了 MVCC(多版本并发控制):每次写入不是覆盖旧值,而是生成一个新版本,旧版本继续保留一段时间。这样读请求可以安心地读自己出发时的那个版本,写请求也可以在另一个版本上继续写,真正做到「读不阻塞写、写不阻塞读」。旧版本则由后台 GC(垃圾回收)进程定期清理。

回到茶饮店:TiKV 的货架整齐摆放着「一杯一杯」的完整配方,做一杯茶只需取一整格原料,所以点查(按主键取一行)极快。MVCC 则像配方册子只加新页、不改旧页——读的人看的永远是下单那一刻的配方,写的人继续在后面补新页,各不干扰。

Stotal≈V×Srow\displaystyle S_{total} \approx V \times S_{row}
MVCC 带来的代价:存储总占用约等于「版本数量 × 单行大小」。版本越多,额外存储开销越大;这也是为什么需要 GC 定期回收旧版本。
  1. 总存储占用 $S_{total}$数据在磁盘上实际占用的空间,包含多个 MVCC 版本,比逻辑数据量要大。
    Stotal\displaystyle S_{total}
  2. 版本数量 $V$同一行数据因多次更新而保留的历史版本个数。每写一次就多一个版本,$V$ 会随写入频率增长。
    V\displaystyle V
  3. 单行大小 $S_{row}$一行数据(包含所有列)的字节数。MVCC 下每个版本都保存一份完整或部分数据,因此总占用大约等于所有版本大小之和。
    V×Srow\displaystyle V \times S_{row}

Raft一致性协议:三家仓库对账的规矩

现在你有了多个中央仓库,但新的担心出现了:如果 A 仓库的珍珠被烧毁了,顾客的「珍珠奶茶」怎么办?于是你规定:每份重要原料(Region)必须同时在三个仓库各存一份,而且三份必须保持一致。这里的规矩就是 Raft 协议:三个仓库组成一个小组,其中一个被指定为「主仓」(Leader),另外两个是「从仓」(Follower)。顾客下单时,主仓把订单记进自己的台账,同时把同一笔订单广播给两个从仓,要求它们也记进台账。

Raft 的核心原则是「多数派决策」:主仓发出的订单日志,只要超过半数的仓库(3 个中至少 2 个)确认记好了,这笔订单就算成功提交,可以告诉顾客「下单成功」。剩下的那个仓库慢一点、甚至暂时宕机,都不影响这笔订单的成功。如果主仓突然失联,两个从仓会发起新一轮选举,票数过半的一方成为新主仓,继续服务。

这套机制的关键在于「少数服从多数」:只要多数派(quorum)活着,数据就不会丢,服务就能继续。副本数量越多容错能力越强,但成本和同步开销也越大。TiDB 默认每个 Region 保存 3 个副本,这恰好是「能容忍 1 个副本故障」的最小配置。

放在奶茶店场景里:三仓对账制度保证了「只要两家仓库还活着,你的奶茶配方就丢不了」。Raft 协议不是玄学,它就是一套清晰的「记账 + 投票」规则,让分布式系统在部分机器故障时依然稳如泰山。

N=2f+1,quorum=f+1\displaystyle N = 2f + 1,\quad quorum = f + 1
想要容忍 f 个节点同时故障,至少需要 2f+1 个副本;系统能正常工作的条件是有 f+1 个节点(即多数派)存活。
  1. 容忍故障数 $f$允许同时宕机的副本数量。f=1 表示允许 1 个副本故障,服务依旧可用。
    f\displaystyle f
  2. 最少副本数 $N = 2f + 1$为容忍 f 个故障,副本总数必须是 2f+1。f=1 时 N=3,这就是 TiDB 三副本的由来。
    N=2f+1\displaystyle N = 2f + 1
  3. 多数派 $quorum = f + 1$系统能够提交数据所需的最少确认数。f=1 时 quorum=2,即 3 个副本中至少 2 个确认,写才成功。
    quorum=f+1\displaystyle quorum = f + 1

TiFlash列式引擎:总部的分析台账

中央仓库解决了「做茶快」,但老板又提出新需求:我想知道上周哪种水果茶的销量最高、哪个门店的利润最薄。如果让点单员从三万个「整杯配方」的货格里翻找,查「所有配方里的水果用量」,效率简直灾难——因为分析往往只关心少数几个字段,却要扫描所有行的全部列。于是总部新设了一个「分析中心」:不按「一杯茶」记账,而是把「所有配方里的水果用量」单独记成一列台账,再按品类记账。

这就是 TiFlash 的列式存储:同一列的数据连续存放,读取时只需加载查询涉及的列,再配合列式压缩算法(同一列数据类型相同,压缩率远高于行式数据),分析查询的磁盘 IO 和 CPU 开销都大幅降低。列存天生为「大范围扫描+聚合统计」而生。

但 TiFlash 的数据从哪来?它不能自己造数据,而是以 Raft Learner 的身份挂在每个 Region 的 Raft 组里。Learner 是一个特殊角色:它像从仓一样接收并应用主仓广播的日志,但不参与投票——因为它只是用来做分析的副本,投不投票不影响数据一致性,这样即使它暂时掉队,也不会拖累线上写入。TiFlash 通过异步复制主仓的 Raft 日志,把 TiKV 中的每一行变更同步到自己的列式存储中,做到近乎实时的数据新鲜度(秒级延迟)。

分析中心的台账虽然不负责做茶,但因为它专精「按类统计」,老板的报表需求几分钟就能出。TiFlash 用 Learner 身份「悄悄学习、不添乱」,既不参与投票影响一致性,又保证了分析数据的实时性——这正是 TiDB 敢于在一套系统里同时做交易和分析的底气。

IOscan≈CselectedCtotal×IOfull\displaystyle IO_{scan} \approx \frac{C_{selected}}{C_{total}} \times IO_{full}
列式扫描的磁盘开销约等于「用到的列数 ÷ 总列数 × 全表扫描开销」。只查 2 列而表有 20 列时,IO 开销约为行式全扫的十分之一。
  1. 选中列数 $C_{selected}$查询实际需要的列数量,例如统计平均年龄只需要 age 一列。
    Cselected\displaystyle C_{selected}
  2. 总列数 $C_{total}$整张表包含的所有列数量。行式存储即使只查一列,也必须读出整行;列式只需读选中列。
    Ctotal\displaystyle C_{total}
  3. IO 开销 $IO_{scan}$扫描数据产生的磁盘读写量。比例越小,列式存储的优势越大。
    CselectedCtotal×IOfull\displaystyle \frac{C_{selected}}{C_{total}} \times IO_{full}

HTAP混合负载:一套系统,两套账本

以前的 IT 系统通常要建两套:一套 OLTP(在线交易)负责处理订单、扣库存,一套 OLAP(在线分析)负责跑报表。数据得靠 ETL 任务每天深夜从交易库搬到分析库,老板看到的水远是「昨天的数据」。TT 茶饮想做一个改变:交易和分析用同一套系统,点单产生的数据秒级出现在分析台账里,老板随时能看「此刻哪款茶卖得最快」。这就是 HTAP(混合事务与分析处理)。

TiDB 的 HTAP 能力建立在「行列双格式」之上:同一份数据,在 TiKV 中以行式存储服务点查和小事务,在 TiFlash 中以列式存储服务聚合分析。当业务发来一条 SQL,TiDB 优化器会像一个「智能调度员」,先看查询的特征——如果是一个简单的主键点查,它走 TiKV,几十毫秒返回;如果是一个大表分组汇总,它走 TiFlash,利用列存和向量化计算快速出结果。优化器通过估算两种引擎的执行代价来选择更优路径,开发者也可以手工指定 Hint 强制选路。

两个引擎的数据通过 Raft 日志保持同步,TiKV 提交一笔写入,TiFlash 几乎同时通过 Learner 应用同一条日志。更妙的是,TiDB 在资源层面做了隔离与管控:分析任务跑在 TiFlash 上,不会抢占 TiKV 的交易资源,交易峰值时报表照跑不误。这就是「一套系统,两套账本,实时同步,互不干扰」。

回到茶饮集团:前台点单照常用「整杯配方」的取料方式(行存交易),老板的实时报表则从「品类台账」(列存分析)直接出数。两本账各司其职,数据又实时互通。TiDB 用一套系统干了过去两套系统的活,还不用等第二天的 T+1 数据——这就是现代 HTAP 数据库的魅力。

E∗=arg⁡min⁡e∈{TiKV, TiFlash}Coste(query)\displaystyle E^* = \arg\min_{e \in \{TiKV,\ TiFlash\}} Cost_e(query)
优化器对每条 SQL 评估「如果走 TiKV 要花多少代价」和「如果走 TiFlash 要花多少代价」,然后选择代价更小的引擎执行。
  1. 备选引擎 $e$TiKV 行存引擎与 TiFlash 列存引擎两个候选。每个都有各自的擅长场景。
    e∈{TiKV, TiFlash}\displaystyle e \in \{TiKV,\ TiFlash\}
  2. 代价值 $Cost_e(query)$在引擎 $e$ 上执行该查询的估算成本,包括 IO、CPU、网络传输等。
    Coste(query)\displaystyle Cost_e(query)
  3. 取最小值 $\arg\min$在所有候选引擎中挑出代价最小的那个。如果点查便宜就走 TiKV,如果全表聚合便宜就走 TiFlash。
    arg⁡min⁡eCoste(query)\displaystyle \arg\min_{e} Cost_e(query)

AI 应用场景

电商交易与实时经营分析一体化

阶段:数据底座与特征存储建设

解决的问题:电商平台同时存在高频订单写入(交易库)和实时 GMV、转化率分析(分析库)。传统方案用 MySQL 存交易、用数仓做 T+1 分析,数据延迟一天,运营无法快速调整。TiDB 的 HTAP 让订单数据写入 TiKV,秒级同步到 TiFlash,运营看板直接查列存,省去 ETL 管道。

具体案例:某头部电商大促期间每秒写入数万订单,TiDB 弹性扩展 TiDB Server 承接流量,TiFlash 同步订单明细供 BI 实时统计;AI 推荐系统也直接读取 TiFlash 中的用户行为聚合特征,无需另建特征宽表管道。

数据库选型与容量规划

阶段:基础设施规划与成本评估

解决的问题:技术决策者面对 MySQL 分库分表、NewSQL 和 NoSQL 时难以权衡。TiDB 的存算分离架构让「容量规划」变得简单:计算层按 QPS 估算节点数,存储层按数据量估算 TiKV 节点,分析负载再评估 TiFlash 节点,三类资源互不耦合。

具体案例:某金融科技公司在 POC 测试中用 TPC-C 压测写入、用 TPC-H 压测分析,再结合备份恢复演练,得出「3 台 TiDB Server+5 台 TiKV+2 台 TiFlash」的部署方案,相比老方案节省 40% 的整体成本。

AI 应用的高可用数据底座

阶段:在线推理服务的数据读取链路

解决的问题:AI 在线推理(如实时反欺诈、智能客服)需要低延迟读取用户特征,同时要求数据库高可用。TiKV 的 Raft 三副本保证单节点故障不丢数据,PD 自动转移 Leader 让读写不中断,满足 AI 服务 7×24 小时的可用性诉求。

具体案例:某风控系统用 TiDB 存储用户行为特征,Raft 保证三地机房副本同步,单机房故障时请求自动切到新 Leader;特征更新通过 TiFlash 近实时同步,模型训练读取列存样本,推理读取行存特征,一条数据管道同时服务训练与在线推理。

Python 演示

python77 行
# -*- coding: utf-8 -*-"""TTDB: 模拟 TiDB 分布式写入与行列混合读取的教学示例"""import hashlib REGION_NUM = 2          # 模拟 2 个 RegionREPLICAS = 3            # Raft 三副本  def hash_key(key):    return int(hashlib.md5(key.encode("utf-8")).hexdigest()[:8], 16)  class RaftGroup:    def __init__(self, group_id):        self.group_id = group_id        self.logs = [[] for _ in range(REPLICAS)]  # 三个副本各自的日志        self.commit_index = 0     def append_entry(self, entry):        for i in range(REPLICAS):            self.logs[i].append(entry)      # 日志复制到所有副本        self.commit_index += 1        return self.commit_index            # 返回已提交位置  class Region:    def __init__(self, region_id):        self.region_id = region_id        self.rows = {}                       # 行式存储: key -> row        self.group = RaftGroup(region_id)     def put(self, key, row):        ok = self.group.append_entry(("put", key, row))        if ok < 2:            raise RuntimeError("副本未达多数派")        self.rows[key] = row                 # 提交后落盘(模拟 TiKV)  class ColumnarStore:    def __init__(self):        self.columns = {}                    # 列式存储: col_name -> [value]        self.sync_count = 0     def apply(self, row):        for col, val in row.items():            self.columns.setdefault(col, []).append(val)        self.sync_count += 1     def avg(self, col):        vals = self.columns.get(col, [])        return sum(vals) / len(vals) if vals else 0  def route(key, region_num):    return hash_key(key) % region_num  def main():    regions = [Region(i) for i in range(REGION_NUM)]    columnar = ColumnarStore()    for i in range(8):        key = "user_%d" % i        row = {"age": 20 + i, "city": "City_%d" % i}        rid = route(key, REGION_NUM)        regions[rid].put(key, row)          # 写入行存 TiKV        columnar.apply(row)                 # 同步到列存 TiFlash     key = "user_3"    rid = route(key, REGION_NUM)    row = regions[rid].rows[key]            # 点查: 只访问一个 Region    print("点查 user_3 ->", row)     print("全表平均 age =", columnar.avg("age"))  # 列存聚合  if __name__ == "__main__":    main()
目的:演示 TiDB 的关键机制——哈希路由找到 Region、三副本日志复制达成多数派、行存负责点查、列存负责聚合分析。
  1. hash_key(key)对主键做哈希,再取模路由到具体 Region,类似 PD 的路由表的简化版。
  2. self.logs = [[] for _ in range(REPLICAS)]每个 Raft 副本有一条独立日志列表,模拟主仓广播日志给两个从仓。
  3. self.logs[i].append(entry)将同一条写入日志复制到所有副本,对应 Raft 的日志复制阶段。
  4. ok < 2检查提交位置是否达到多数派(3 副本中至少 2 个),否则抛异常,体现「多数派决策」。
  5. self.rows[key] = row行式存储:key 到整行 dict 的映射,点查时一次取出完整一行。
  6. columnar.apply(row)把同一列的值追加到列式存储对应列表中,模拟 TiFlash 的 Learner 异步同步。
  7. regions[rid].rows[key]点查只需访问目标 Region,无需扫描其他 Region。
  8. columnar.avg("age")聚合分析只读取 age 这一列,不需要扫描所有列,体现列存优势。

常见误区

误区我以为 TiDB 既然是分布式数据库,肯定像列存那样把一张表按列拆开,分散到不同机器上存储,这样查询列更快。

正解TiKV 是行式存储,一行数据的完整列始终作为一个整体存放在同一个 Region 中。真正把数据按列拆分存放的是 TiFlash。TiDB 的架构是「行存 TiKV + 列存 TiFlash」双引擎,不是单引擎混合。行存保证点查快,列存保证分析快。

为何常见:很多人把「分布式」和「列式」两个概念混在一起,以为分布式必然要拆行列存放;实际上 TiDB 的分布式是按主键 Range 拆成 Region 分片,每个分片内部仍是完整行。

误区数据要复制到三个副本才算安全,所以写请求必须等三个仓库都确认「记好了」才能告诉客户成功。

正解Raft 只需多数派(3 副本中 2 个)确认即可提交。少数派副本慢、宕机或网络分区,都不阻塞写入,这也是「可用性」的来源。如果要求全部副本确认,任何一个慢节点都会拖垮整体性能。

为何常见:直觉上「复制得越多越安全」,容易误以为「全部成功才叫成功」;但 Raft 的设计精髓是「多数派即代表整体」,牺牲一点冗余度换取性能和可用性。

误区既然 TiFlash 通过 Raft 日志同步,那写入 TiKV 的数据应该立即出现在 TiFlash 里,查分析报表和查交易数据一样实时。

正解TiFlash 是异步应用 Raft 日志,数据同步存在秒级延迟(通常几百毫秒到数秒)。它保证的是「最终一致」,而非「线性一致」。对于实时报表、经营分析,秒级延迟完全够用;但需要强一致的读取,必须走 TiKV。

为何常见:HTAP 宣传让人误以为行列引擎完全同步;实际上 TiFlash 采用 Learner 角色异步复制,刻意与线上写入解耦,以换取分析负载不影响交易性能。

误区HTAP 等于一条查询同时发到 TiKV 和 TiFlash,两边各算一半,再把结果合并返回,所以查询特别慢。

正解TiDB 优化器会为每条 SQL 选择一条最优执行路径——要么走 TiKV,要么走 TiFlash,不会同时跑两遍。只有跨引擎的复杂查询(如 join 数据分布在两个引擎)才需要协调,但这也是优化器全局规划后的结果,并非每条查询的默认行为。

为何常见:看到「混合」两个字,容易理解成「两种存储同时参与」;实际上 HTAP 的「混合」指一个系统同时支持两类负载,而不是一条查询混合两种存储。

误区PD 就是类似 Redis 的路由缓存,记录 Region 位置而已,挂了重新启动或者让其他节点顶上就行,数据库还能正常写。

正解PD 是集群的「大脑」,不仅存路由元数据,还负责分配全局时间戳 TSO、驱动 Region 调度和故障转移。PD 自身也用 Raft 组成多数派集群,一旦 PD 集群整体不可用,整个 TiDB 集群将无法写入新数据、无法路由。它是高可用架构中的最关键节点。

为何常见:人们习惯把「元数据服务」和「缓存」等同,忽略了 TSO 与调度职责;实际上 PD 的可用性直接决定集群可用性。

面试题

Q1. TiDB 属于哪一类数据库?

单选题 · easy

  • A.分布式 NewSQL 数据库← 正确答案
  • B.传统单机关系数据库
  • C.文档型 NoSQL 数据库
  • D.图结构专用图数据库

答案:A。TiDB 能像传统关系库一样支持 SQL 和 ACID 事务,又能像分布式 NoSQL 那样水平扩展,这正是 NewSQL 的定义。它虽然兼容 MySQL 协议,但底层是分布式架构,不是单机关系库,也不是文档型或图数据库。

Q2. TiDB 计算层(TiDB Server)的主要特性是?

单选题 · easy

  • A.无状态可横向扩展← 正确答案
  • B.自带存储引擎副本
  • C.只负责生成执行计划
  • D.必须与存储同机部署

答案:A。TiDB Server 不保存业务数据,是无状态的,可以随意增删节点,前面挂负载均衡即可扩容。它虽然会生成执行计划,但还要负责接收请求、与 PD 交互,不能说「只负责」;它与存储层分离部署,不是必须同机。

Q3. TiDB 集群中「大脑」PD 的核心职责不包括?

单选题 · easy

  • A.分配全局时间戳 TSO
  • B.维护 Region 路由信息
  • C.负责实际业务数据存储← 正确答案
  • D.进行负载均衡与调度

答案:C。PD 负责元数据管理、TSO 分配、Region 调度与故障转移,相当于总调度台;但实际业务数据存储在 TiKV/TiFlash 中,PD 本身不存业务数据。这道题考察对「调度中枢 vs 存储引擎」职责边界的理解。

Q4. 关于 TiDB 的 Region 分片,下列说法正确的是?

单选题 · mid

  • A.按主键范围连续切分← 正确答案
  • B.按写入时间随机切分
  • C.一个 Region 只能存一行
  • D.Region 大小没有上限

答案:A。Region 按主键 Range 左闭右开切分,范围连续且不重叠,方便路由和分裂合并。它包含多行数据,默认上限约 96MB;不是随机切分也不是按时间切分,超过上限会触发分裂。

Q5. TiKV 底层存储引擎采用什么结构?

单选题 · mid

  • A.LSM-Tree 追加写结构← 正确答案
  • B.纯 B+ 树定点更新结构
  • C.跳表加哈希索引结构
  • D.倒排索引全文检索结构

答案:A。TiKV 基于 RocksDB,底层是 LSM-Tree,通过内存表批量刷盘和后台 Compaction 把随机写变成顺序写。B+ 树是传统关系库的方案;跳表哈希多见于 NoSQL 缓存;倒排属于全文搜索引擎。

Q6. Raft 协议三副本配置最多容忍几个节点宕机?

单选题 · mid

  • A.允许一个副本宕机← 正确答案
  • B.允许两个副本宕机
  • C.允许三个副本全部宕机
  • D.任何副本都不允许宕机

答案:A。三副本的多数派是 2 个,只要有 2 个副本存活就能选举 Leader 并提交日志,因此最多容忍 1 个节点宕机。如果 2 个副本宕机,系统只剩 1 个节点,无法达成多数派,会失去写入能力。

Q7. TiFlash 以什么角色参与 Raft 数据同步?

单选题 · mid

  • A.Raft Learner 只学不投← 正确答案
  • B.Raft Leader 独立写日志
  • C.备份节点定期全量复制
  • D.主从同步的同步从库

答案:A。TiFlash 以 Learner 角色挂载到 Raft 组,接收并应用主仓广播的日志,但投票时没有发言权。Leader 是负责写入的角色;全量复制和主从同步都不是 Raft 体系内的概念。

Q8. 以下关于 TiFlash 列式存储的特点,正确的有?

多选题 · mid

  • A.按列连续存放并压缩← 正确答案
  • B.适合大范围聚合分析← 正确答案
  • C.数据来自 Raft 日志同步← 正确答案
  • D.每笔写入实时强制落盘

答案:ABC。列式存储将同列数据连续存放,便于压缩和向量化计算,适合聚合分析;数据由 Learner 角色通过 Raft 日志异步同步。D 错误,TiFlash 是异步应用日志,并非每笔写入实时落盘。

Q9. HTAP 场景下 TiDB 优化器如何选择执行引擎?

多选题 · hard

  • A.估算行存与列存的代价← 正确答案
  • B.可通过 Hint 手工指定← 正确答案
  • C.所有查询强制走列存
  • D.事务请求永远走 TiFlash

答案:AB。优化器对每条 SQL 分别估算走 TiKV 和走 TiFlash 的代价并择优;也支持用 Hint 人工干预选路。C、D 错误:并非所有查询强制列存,事务请求默认走行存 TiKV,只有分析型请求才可能选列存。

Q10. 关于 TiDB 一致性与可用性,下列说法正确的有?

多选题 · hard

  • A.多数派提交后写请求成功← 正确答案
  • B.少数派故障不影响读写← 正确答案
  • C.PD 集群本身就靠多数派← 正确答案
  • D.数据丢失需人工恢复副本

答案:ABC。Raft 的核心是多数派决策,提交只需多数派确认,少数派故障不影响读写,因此 A、B 正确;PD 自身也组 Raft 多数派集群,C 正确。D 错误:副本故障时 PD 会自动调度新副本,无需人工恢复。