想象你是一家全国连锁快递公司的负责人,客户遍布几百个城市,每天产生上亿个包裹。你不可能把所有包裹堆在一个仓库里——那迟早爆仓;你也不可能让每个门店各自管理仓库——那样快递无法跨城配送。于是你设计了“门店—仓库—调度中心”三层体系:门店负责收件发件,仓库负责分拣存储,调度中心负责把包裹分配到最合适的仓库。TiDB就是数据库世界里的这套三层体系——它把数据库拆成了“SQL门店层”“KV仓库层”和“PD调度中心”,让数据规模像快递网点一样无限扩张。更妙的是,客户手里的“寄件单”(MySQL协议)完全不用改,他们甚至感觉不到后台已经换成了一台分布式巨兽。

岗位关联

对 AI 从业者而言,理解 TiDB 的存储与调度原理,意味着你掌握了云原生时代“数据量大了怎么办”的核心方法论——这几乎是大模型训练数据管线、推荐系统特征存储、A/B 实验平台等所有 AI 基础设施的底座技能。

目录
  1. 可视化演示
  2. TiDB架构总览
  3. TiKV存储引擎
  4. Raft一致性协议
  5. Region分片机制
  6. PD调度中心
  7. 分布式事务
  8. 部署与实践
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

演示 TiDB 三层分布式架构:计算存储分离,TiKV 多节点 Raft 共识保证强一致,PD 调度 Region 分裂与均衡,实现水平扩展与 HTAP 混合负载。

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

三层架构解耦 TiDB计算 + TiKV存储 + PD调度 计算存储分离 水平扩展,保持MySQL协议兼容

TiDB架构总览

想象你经营的那家连锁快递公司:每个城市都有门店负责收件发件,几大区域各有一座自动化仓库负责存件,总部还有一间调度办公室记录全局信息。TiDB的设计几乎是这套结构的翻版——TiDB Server就是那些“门店”,TiKV是“仓库”,PD是“调度办公室”。三者各司其职,互不拖累,这正是它区别于传统单机数据库的第一性原理。

传统MySQL里,一台服务器既当“门店”又当“仓库”,数据一多就爆仓。TiDB的第一步改革就是拆开这两件事:TiDB Server是纯计算层,只负责解析SQL、生成执行计划、把请求路由到正确的数据节点;TiKV是纯存储层,只负责以键值对形式持久化数据。由于计算层不存任何数据,它天然“无状态”——你可以随时增加十台八台TiDB Server来分担查询压力,就像旺季多开几个临时窗口,完全不用搬动仓库里的任何包裹。

这带来了两个直接收益。第一,水平扩展变得极简单:计算不够加计算节点,存储不够加存储节点,互不干扰。第二,TiDB向上兼容MySQL协议,你现有的MySQL客户端、连接池、ORM框架几乎零改动就能接入。客户看到的还是熟悉的“寄件单”,只是背后处理订单的从一家小店变成了一整张全国物流网。当然,“拆开”只是第一步,一个SQL查询被拆成哪些子任务、由哪些TiKV节点协作、数据散落何处,这些复杂性被PD与TiKV的底层机制稳稳接住了,接下来我们逐层拆解。

回到快递公司:门店加多了,接单能力自然上去,但包裹能否准时送达,还得看后端的仓库和调度。TiDB的奥义正是“各管一摊,摊摊可扩”。

QPStotal≈ntidb×QPSnode\displaystyle QPS_{total} \approx n_{tidb} \times QPS_{node}
系统总吞吐 ≈ 计算节点数量 × 单节点吞吐量。计算层无状态,所以节点数可以随负载自由伸缩,这是TiDB水平扩展能力的数学底座。
  1. QPS_total(系统总吞吐)整个TiDB集群每秒能处理的SQL查询数量,是衡量数据库整体能力的核心指标。
    QPStotal\displaystyle QPS_{total}
  2. n_tidb(计算节点数)TiDB Server实例的个数。因为计算层不落盘、无状态,所以n可随时增减。
    ntidb\displaystyle n_{tidb}
  3. QPS_node(单节点吞吐)单个TiDB Server每秒能处理的查询量。所有节点一起干活,总吞吐近似等于单点能力之和。
    QPSnode\displaystyle QPS_{node}
  4. 近似等于(≈)真实网络有调度开销与数据搬迁损耗,因此用近似号而非严格的等号。
    ≈\displaystyle \approx

TiKV存储引擎

现在走进那间自动化仓库——TiKV。货架上每一格都有唯一编号(key),格子里放着对应商品(value),这就是最朴素的“键值对”概念。TiKV本质上是一个分布式Key-Value存储引擎,所有表数据最终都会变成键值对,按key排序后存进无数个TiKV实例。

但仓库有个麻烦:盘点商品(读操作)时,不能因为某个工人正在上架新货(写操作)就暂停整个仓库的运转。TiKV引入MVCC(多版本并发控制)解决这个矛盾——每个key的每一次修改都会生成一个新版本“入库记录卡”,卡片上盖着写入时间戳(ts)。读操作只需找到“不晚于自己读时间戳”的最新版本,就可以无锁地读到一份一致性快照。正在写入的事务对其他读者不可见,读写互不阻塞,就像理货员看到的历史账本永远是一致完整的。

底层落盘方面,TiKV采用LSM-Tree结构。新写入先进入内存中的“跳表”,攒够一批再批量刷到磁盘,形成分层的数据文件。LSM-Tree把随机写转化为顺序写,让机械硬盘和SSD都能发挥最大顺序吞吐——这正是TiKV能扛住高并发写入的隐藏武器。打个比方:仓库不会为每个小包裹单独跑一趟车,而是装满一整车再发,运输效率因此大幅提升。

仓库里的每个货架格都贴着若干张不同时间的“入库记录卡”,读的人挑最新的看,写的人继续贴新卡,彼此不抢笔——这就是MVCC的温柔之处。

Read(key,ts)↦Vlatests.t.tslatest≤ts\displaystyle Read(key, ts) \mapsto V_{latest} \quad \text{s.t.} \quad ts_{latest} \le ts
在时间戳ts下读取key时,返回的是所有已提交版本中“时间戳不超过ts”的最新值。MVCC让读写各看各的版本,互不干扰。
  1. Read(key, ts)(带时间戳的读取)MVCC下的读操作必须携带一个时间戳ts,表示“我想看到这一刻为止的数据”。
    Read(key,ts)\displaystyle Read(key, ts)
  2. V_latest(最新版本值)返回值是满足条件的所有版本中最新的一份,而不是随意的一个旧版本。
    Vlatest\displaystyle V_{latest}
  3. 约束条件 ts_latest ≤ ts(版本时间戳上界)只能看到读时间点之前已经提交的版本,未提交事务被天然隔离,因此读写不互相阻塞。
    tslatest≤ts\displaystyle ts_{latest} \le ts

Raft一致性协议

仓库里最重要的资产是数据,绝不能因一台机器宕机就丢失。TiKV的解法是:每个“货架区”(Region)的数据都复制三份,放在不同机房的TiKV节点上,三个副本组成一个小队,叫Raft Group。小队里有一个“队长”(Leader)和两个“队员”(Follower),所有写请求必须先到队长那里。

Raft协议要解决的核心问题是:三个副本都声称自己是最新的,听谁的?答案是“多数派说了算”。队长收到写入请求后,先把日志(写操作记录)发给两个队员,当至少2个节点(超过3个的一半)确认收到后,这条日志才算真正提交。为什么多数派规则如此重要?因为在任何时刻,不可能同时存在两个“过半数”的阵营——就像快递公司开全国会议,只要超过半数门店到会,会议就有效,且不可能同时有另一场也过半数到会的“分会”被认定为有效。

如果队长宕机,剩下的两个队员中会快速选举出一个新Leader,继续对外服务。整个过程对业务几乎透明:客户端只需访问Leader,Leader挂掉后自动切换。这就是Raft的三大核心动作——Leader选举、日志复制、多数派提交——共同保障了分布式环境下数据的强一致性,同时容忍单节点故障。快递公司里货架区哪怕有一个仓库失火,另外两个仓库的存根也能还原一切。

多数派不是简单的“少数服从多数”,而是用数学上的超半数保证了任何两个决策阵营必然有交集——这是分布式系统互斥共识的基石。

q=⌊N2⌋+1\displaystyle q = \left\lfloor \frac{N}{2} \right\rfloor + 1
一个Raft组要提交一条日志,至少需要q个节点确认。N=3时q=2,恰好能容忍1个节点故障。
  1. N(节点总数)Raft组内的全部节点数,TiDB中每个Region默认有3个副本。
    N\displaystyle N
  2. 向下取整 ⌊N/2⌋N除以2后向下取整,目的是计算出“不到一半”的节点数。
    ⌊N2⌋\displaystyle \left\lfloor \frac{N}{2} \right\rfloor
  3. +1(超半数的关键)在“一半”的基础上再加1,确保q严格大于N的一半,这就是“多数派”的定义。
    +1\displaystyle +1
  4. q(提交所需票数)当确认日志的节点数≥q时,日志才允许Commit并对客户端返回成功。
    q\displaystyle q

Region分片机制

仓库再大也有装满的一天。TiDB把数据按key的连续范围切分成一个个标准尺寸的“货架区”,每个货架区称为一个Region,默认上限96MB。当某块货架区数据持续写入、逼近容量时,它会自动从中间“一分为二”,变成两个相邻但完全独立的Region。这就是TiDB数据自动分片的核心机制:Region分裂。

分裂过程非常轻量:TiKV检测到Region超过阈值后,在本地选定一个分裂点,把原key区间[a, c)切成[a, b)和[b, c)两段,各自生成新的Region元数据。分裂本身几乎不搬动数据——只是逻辑上的一刀,就像在快递柜里插入一块隔板,把一格变成两格,货物不用搬出去。分裂后,PD调度中心会为新的Region补充副本,并把多余的副本迁移到其他TiKV节点上,让整个集群的负载保持均衡。

更关键的是,Region是TiDB数据均衡与容灾的最小单位。每个Region都有独立的Raft三副本,分布在不同的可用区或机房。某个机房整体断电,该Region的Leader会瞬时切换到另一个机房的副本上,业务无感知。Region分裂让数据像水流一样自然摊平在所有节点上,新增机器后PD会自动把一部分Region调度到新节点,无需任何人工拆库拆表。这也是TiDB对比传统分库分表中方案最优雅的地方——分片不是DBA设计的,而是数据自己“裂”出来的。

货架区的“自动裂变”让TiDB无需人工分库分表就能水平扩展——你只管往仓库里堆货,货架自己会分格,调度员会自动把货架匀到各个分仓。

split(R)={RL,RR},RL∩RR=∅,RL∪RR=R\displaystyle split(R) = \{R_L, R_R\}, \quad R_L \cap R_R = \varnothing, \quad R_L \cup R_R = R
一个Region分裂成左右两个新Region:两者key范围互不重叠,合起来又恰好覆盖原来整个范围。
  1. R(原Region)一个连续的key区间,如 [001, 500)。
    R\displaystyle R
  2. R_L 与 R_R(左右新Region)分裂后产生的一对Region,R_L负责较小key段,R_R负责较大key段。
    RL,  RR\displaystyle R_L, \; R_R
  3. 交集为空(互不重叠)任意一个key只属于其中一个Region,绝不会同时属于两个。
    RL∩RR=∅\displaystyle R_L \cap R_R = \varnothing
  4. 并集等于原Region(无数据丢失)所有原Region中的数据在分裂后依然存在,只是被分到了两个新货架区。
    RL∪RR=R\displaystyle R_L \cup R_R = R

PD调度中心

PD是快递总部的“指挥室”,全称Placement Driver。它手里握着一张“大数据地图”:每一个Region的副本分别在哪台TiKV节点上、谁是Leader、节点健康状况如何。当TiDB Server收到一条SQL,需要第一时间向PD询问“这笔数据在哪个仓库的哪块货架区”,PD回复之后SQL才能精准路由。

PD的第二项核心职责是调度。它持续收集各TiKV节点的Region数量和负载指标,一旦发现某节点Region过多或磁盘即将写满,就会挑选若干Region,把它们的副本迁移到空闲节点上,再把Leader也转移过去。整个过程完全后台自动运行,业务无感知。这就像总部看到A仓库货架快塞满、B仓库空空如也,会立刻安排货车把一部分货架整体搬过去。

PD的第三项职责是分发全局时间戳TSO。分布式环境下,多个节点同时发起事务,必须有一个全集群统一的“时钟”来为事务排队和排序,否则“先写后读”的因果顺序就乱了。PD通过“物理时间左移18位+逻辑计数”的方式,为每个事务分配一个全局唯一且严格递增的时间戳。所有节点用这个TSO作为MVCC和多版本的时间标签,就仿佛全国所有快递单都盖着总部的统一钢印——任何两单之间都能比出先后。

调度中心不搬一件货,但它知道每件货在哪,决定货往哪搬,还给每单盖上唯一的“时间钢印”——这就是PD的三重身份:地图、调度员、钟表匠。

TSO=tphy×218+tlog\displaystyle TSO = t_{phy} \times 2^{18} + t_{log}
PD将物理毫秒时间戳左移18位,再叠加一个逻辑计数器,拼出全局唯一且单调递增的时间戳。
  1. t_phy(物理毫秒时间)从1970年起算的当前毫秒数,提供TSO的物理时间基准。
    tphy\displaystyle t_{phy}
  2. 2^18(左移18位)为同一毫秒内的逻辑计数预留空间,相当于给“秒针”下面再加一个更细的“毫秒针刻度”。
    218\displaystyle 2^{18}
  3. t_log(逻辑计数)同一物理毫秒内每发一个TSO就加1,保证同一时刻并发请求也能分出先后。
    tlog\displaystyle t_{log}
  4. 整体相加(唯一递增)物理时间不同则TSO必然不同;物理时间相同时逻辑计数决定顺序,因此全局单调递增。
    tphy×218+tlog\displaystyle t_{phy} \times 2^{18} + t_{log}

分布式事务

快递公司里有一个最棘手的场景:一个订单涉及到多个货架区的商品——比如买手机的同时办了合约套餐,手机在A货架区,套餐权益在B货架区。要保证这笔订单要么全部生效、要么全部不生效,绝不能出现手机发货了、套餐没办上的尴尬。TiDB用“两阶段提交”(2PC)解决跨Region的原子性问题。

第一阶段叫Prepare(预占):事务协调者把“预占通知”发给所有涉及到的Region,每个Region在本地的Raft组内完成预写和锁标记,然后回复“准备好了”。如果有任何一个Region无法完成预占(比如锁冲突),协调者就广播Abort,所有Region回滚,不留痕迹。第二阶段叫Commit(确认):所有Region都回复Ready后,协调者广播Commit,各Region正式提交。TiDB的2PC还有一个巧妙细节:事务的提交时间戳TSO在Prepare阶段就已确定,所有Region都用同一个TSO提交,保证全局一致的快照视图。

TiDB的事务模型默认采取乐观锁思路——事务执行过程中不长时间持有锁,只在最终提交时检查冲突。如果冲突发生,让事务重试而不是死等,配合PD分发的TSO做冲突检测。在多数场景下,一个事务涉及的数据如果恰好都落在同一个Region内,TiDB会自动退化为“本地事务”,直接一轮Raft提交搞定,连两阶段都不用走。这也是为什么TiDB作为分布式数据库,在单点小事务上与单机MySQL的差距并不悬殊。

跨货架区的订单,靠的就是“先各方预占、再统一确认”的流程——想象所有分店先盖章确认“这批货我先锁定了”,总部收到全部盖章后才正式放行,任何一家反悔则全单作废。

Txn:{Prepare→Commit,所有Region预占成功Prepare→Abort,任一Region预占失败\displaystyle Txn: \begin{cases} Prepare \to Commit, & \text{所有Region预占成功} \\ Prepare \to Abort, & \text{任一Region预占失败} \end{cases}
分布式事务的两种结局:要么所有参与的Region一起提交,要么一起回滚,不存在中间状态。
  1. Txn(事务)一组原子读写操作,要么全部成功,要么全部不生效。
    Txn\displaystyle Txn
  2. Prepare → Commit(成功路径)协调者先向所有Region发送预占请求,全部返回Ready后进入提交阶段。
    Prepare→Commit\displaystyle Prepare \to Commit
  3. Prepare → Abort(失败路径)只要有一个Region预占失败或超时,协调者就广播回滚,所有Region释放锁并丢弃预写日志。
    Prepare→Abort\displaystyle Prepare \to Abort
  4. 分支条件右侧的两个条件分别对应两种结局的触发前提,确保事务只有唯一的最终状态。
    所有Region预占成功/任一Region预占失败\displaystyle \text{所有Region预占成功} / \text{任一Region预占失败}

部署与实践

理解了TiDB的原理,最后看如何落地。早期部署分布式数据库往往令人头大:要装好几个组件、配置网络、处理高可用,门槛极高。TiDB官方推出的TiUP工具让这一切变得像用手机点外卖一样简单——一条命令即可拉起一个高可用集群,生产环境最少3台机器就能跑起来,单机还能用tiup playground起一个仿真小集群用于学习开发。

水平扩展是TiDB最直观的实践体验:当集群容量不足时,新加几台TiKV节点,PD自动把部分Region迁到新节点,整个流程无需停服。你不需要像传统分库分表那样预先规划分片数,也不需要修改任何SQL——Region自己会裂变,PD自己会搬货。TiDB Cloud和TICloud Serverless还提供了全托管模式,连机器都不用自己买,按量付费就能获得一个云原生分布式数据库。

高并发写入场景是TiDB的主场:订单流水、支付流水、IoT设备上报等写多读少的业务,传统单机MySQL通常扛到每秒几千笔写入就开始报警,而TiDB通过把写入分散到大量Region和TiKV节点上,轻松支撑每秒几十万甚至上百万的写入吞吐。运维侧,TiDB Dashboard提供可视化监控页面,Region分布、热点Region、慢查询、GC状态一目了然。整个学习路径可以浓缩为一句实践心法:先用TiUP拉起一个小集群,往里面灌数据,观察Region如何自动分裂,再尝试加一台TiKV节点看看数据如何流动——亲手演示一遍,胜过一百页文档。

快递公司从一个小城扩张到全国,靠的不是把一个仓库无限加高,而是不断开新仓、上新货架、由调度中心自动平衡——TiDB的实践哲学,与这个逻辑完全同构。

lim⁡n→∞T(n)n=k(k>0)\displaystyle \lim_{n \to \infty} \frac{T(n)}{n} = k \quad (k > 0)
无论如何增加节点数,集群总吞吐T(n)与节点数n的比值始终稳定在正常数k上,这就是线性可扩展性。
  1. n(TiKV节点数)集群中的存储节点数量,水平扩展的核心变量。
    n\displaystyle n
  2. T(n)(集群总吞吐)在n个存储节点下,整个集群每秒能处理的事务或写入量。
    T(n)\displaystyle T(n)
  3. 极限符号与比值当n不断增大时,看平均每节点吞吐是否依然稳定,而不衰减。
    lim⁡n→∞T(n)n\displaystyle \lim_{n \to \infty} \frac{T(n)}{n}
  4. k > 0(正比例常数)比值稳定在正数,说明吞吐随节点数同步增长,即“线性可扩展”——这正是云原生架构追求的理想曲线。
    k>0\displaystyle k > 0

AI 应用场景

金融级支付流水的高并发写入

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

解决的问题:支付系统的交易流水表写入量极大,传统单机MySQL在峰值时段秒级写入量可达数十万笔,分库分表中间件让SQL复杂度和维护成本失控。TiDB通过Region自动分裂与多TiKV节点并行写入,把写入压力均匀打散到整个集群,同时借助Raft三副本确保任何一笔流水都不丢失。

具体案例:某头部支付机构的交易流水库,日增量流水超300亿行,原先用几百台MySQL分库分表还时常告警。迁移到TiDB后,存储节点横向扩展到20台,峰值写入吞吐提升到每秒120万笔,应用层SQL完全不用改,仍然使用MySQL协议连接。

AI特征数据的在线存储与回流

阶段:特征工程与在线推理特征存储

解决的问题:推荐系统和广告排序模型需要实时写入大量用户行为特征,并在毫秒级内完成特征查询。TiDB的分布式架构天然支持特征表水平扩展,MVCC机制保证同一用户特征在并发读写下的一致性快照,避免特征错乱。

具体案例:某短视频平台的实时推荐系统,将用户行为特征表存放在TiDB中,单表日新增特征记录超20亿条。AI推理服务通过TiDB的负载均衡路由到不同Region读取特征,P95查询延迟稳定在5毫秒以内,模型训练前的特征回填任务也直接复用同一套集群。

Python 演示

python53 行
import random class RaftNode:    """简化版Raft节点,只模拟日志复制与票数统计"""    def __init__(self, nid, peers):        self.nid = nid        self.peers = peers        self.log = []     def append_entry(self, entry):        """Leader本地追加一条日志"""        self.log.append(entry)     def replicate_to_peers(self, entry):        """复制日志到所有Follower,统计确认票数"""        self.append_entry(entry)        votes = 1  # Leader自己算一票        for peer in self.peers:            if random.random() > 0.05:  # 模拟极少数丢包                peer.log.append(entry)                votes += 1        return votes     def try_commit(self, votes, need):        """票数达到多数派阈值则提交成功"""        return votes >= need  def build_cluster(size=3):    nodes = [RaftNode(i, []) for i in range(size)]    for node in nodes:        node.peers = [p for p in nodes if p.nid != node.nid]    return nodes  def main():    size = 3    need = size // 2 + 1    nodes = build_cluster(size)    leader = nodes[0]    print(f"集群共{size}节点,Raft多数派阈值={need}")     for i in range(1, 8):        entry = "txn-%02d" % i        votes = leader.replicate_to_peers(entry)        ok = leader.try_commit(votes, need)        status = "COMMIT" if ok else "ABORT"        print(f"写入{entry}:{votes}/{size}票->{status}")        assert ok, "日志复制失败,无法提交"  if __name__ == "__main__":    main()
目的:用Python模拟一个3节点Raft组的日志复制过程,演示“多数派提交”如何保证数据一致。
  1. need = size // 2 + 1计算多数派阈值,3节点集群需要2票才能提交。
  2. leader.replicate_to_peers(entry)Leader把新日志发给所有Follower并统计确认票数。
  3. ok = leader.try_commit(votes, need)当确认票数达到阈值时日志才提交,否则整体失败。
  4. peer.log.append(entry)Follower在本地落一条日志,模拟数据副本的写入。
  5. status = "COMMIT" if ok else "ABORT"用状态标识直观展示每条日志的最终结论。

常见误区

误区听说TiDB兼容MySQL协议,就以为它底层还是MySQL,只是在外面加了一层分库分表路由,本质上和ShardingSphere这类中间件差不多。

正解TiDB的存储层是完全自研的TiKV分布式Key-Value引擎,底层用的LSM-Tree,跟MySQL的InnoDB没有任何关系。TiDB只是“讲MySQL的方言”(兼容通信协议和SQL语法),但内核从计算到存储全链路自研。这就像一家快递公司换了全新的分拣机器和货车,但客户寄件时填的单子还是老样子。

为何常见:因为TiDB对客户端完全透明,用起来太像MySQL,加上“兼容MySQL”的口号让很多人误以为它只是在MySQL外面包了一层壳。

误区3副本的Raft组,只要还有2台机器在线就能正常工作?那我是不是可以放心地让其中一台机器一直宕机?

正解多数派确实意味着“2/3以上节点确认”,但它的深层作用是保证任意两个多数派阵营必然有公共节点。如果你不正确地配置网络分区策略,让2个副本处在同一个分区、1个副本在另一个分区,此时两个分区各自都无法形成多数派,系统只能选择牺牲可用性保证一致性——这恰恰是Raft的安全设计。

为何常见:人们容易把“多数派”理解成简单的少数服从多数投票,而忽略了它在网络分区场景中“互斥”的数学性质。

误区Region分裂要挪动数据文件,会不会像传统分库分表那样需要停机维护?业务会不会一瞬间卡死?

正解Region分裂是逻辑操作,先在TiKV节点上把元数据切成两段,数据文件不需要原地搬迁,耗时通常在毫秒级。分裂之后,PD才会在后台慢慢补充新副本并把副本调度到其他节点,整个过程对客户端完全无感。就像快递柜满了,工作人员快速插入一块隔板变成两格,包裹根本不用搬出去。

为何常见:因为“分裂”这个中文词容易让人联想到物理搬移数据和停机维护,忽略了分布式系统里元数据与数据分离的设计。

误区TiDB是分布式数据库,任何事务都要走两阶段提交,那每笔写操作岂不是都要多轮网络通信?肯定比MySQL慢很多。

正解TiDB的大多数事务只涉及单个Region,会自动走本地事务路径,只需一轮Raft复制即可提交。只有真正跨多个Region的事务才走两阶段提交,并且TiDB还实现了异步提交等优化,让延迟增加有限。就像同城快递当天就到,跨城快递才需要多一天——物流体系不会让每单都按最慢的跨城路径走。

为何常见:把分布式事务的“最坏情况”当成了“所有情况的平均”,忽略了本地事务在TiDB事务中的高占比。

误区TiDB架构图上有TiDB、TiKV、PD、TiFlash这么多组件,学习成本和运维门槛肯定很高,没有专门的DBA团队根本不敢用。

正解TiUP工具提供一键部署,生产环境3台机器就可以组成最小的高可用集群;本地开发还能用tiup playground快速拉起仿真集群。TiDB Cloud甚至提供全托管的Serverless服务,开箱即用、按量付费。快递公司刚开业时“一店一仓一调度”也能跑通,规模大了再扩,TiDB的设计精髓之一就是“从小起步,平滑长大”。

为何常见:被架构图上纷繁的组件名字吓住,误以为所有组件都要大规模部署才够格,忽略了TiDB面向中小团队的低门槛启动设计。

面试题

Q1. TiDB核心架构中,负责解析SQL语句并生成执行计划的组件是?

单选题 · easy

  • A.TiKV存储引擎
  • B.TiDB Server← 正确答案
  • C.PD调度中心
  • D.TiFlash节点

答案:B。TiDB Server是无状态计算层,核心职责就是接收SQL、解析优化、生成执行计划,并把子任务下推给TiKV。TiKV负责存储,PD负责调度元数据,TiFlash是列存分析节点,都不承担SQL解析主责。

Q2. TiDB中每个Region默认的最大容量约为?

单选题 · easy

  • A.64MB
  • B.96MB← 正确答案
  • C.128MB
  • D.256MB

答案:B。Region默认阈值是96MB,超过这个大小就会触发自动分裂。64MB是部分早期版本或其他系统的常见分片尺寸,128MB和256MB则不是TiDB的默认配置,记住"96MB"这个数字即可。

Q3. 一个3副本的Raft组,正常写入需要至少多少个节点确认?

单选题 · easy

  • A.1个节点
  • B.2个节点← 正确答案
  • C.3个节点
  • D.0个节点

答案:B。Raft多数派阈值计算公式是q = ⌊N/2⌋ + 1,3节点代入得q=2。这意味着只要2个节点确认日志就可以提交,允许1个节点故障而不影响服务。

Q4. 下列关于PD(Placement Driver)的描述,错误的是?

单选题 · mid

  • A.PD负责分发全局唯一递增的时间戳TSO
  • B.PD不参与实际数据存储仅管理元数据
  • C.PD直接参与每条SQL的WHERE条件计算← 正确答案
  • D.PD负责Region的负载均衡与故障调度

答案:C。PD是调度中心,负责元数据管理、TSO时间戳分发和负载均衡调度,但它从不参与具体的SQL计算。WHERE条件计算由TiDB Server与TiKV下推执行完成,与PD无关,因此选C。

Q5. TiKV的MVCC机制主要解决什么问题?

单选题 · mid

  • A.提升磁盘顺序写入性能
  • B.让读写互不阻塞并提供一致性快照← 正确答案
  • C.压缩数据以节省存储空间
  • D.加密传输通道并保障安全

答案:B。MVCC多版本并发控制为每个key保留多版本数据,读操作通过时间戳选择自己可见的版本,不需要加锁等待写事务完成。A是LSM-Tree的功劳,C是压缩算法的职责,D是传输安全范畴,均与MVCC无关。

Q6. 当一个Region的数据量超过96MB阈值时,会发生什么?

单选题 · mid

  • A.Region自动合并
  • B.Region自动分裂← 正确答案
  • C.Region被整体删除
  • D.Region暂停写入

答案:B。超过阈值的Region会自动从中间分裂成两个key范围互补的新Region,从而把数据和负载分散开。合并是收缩场景的机制,删除与暂停写入都不存在,所以选B。

Q7. TiDB两阶段提交的第一阶段(Prepare)主要做什么?

单选题 · hard

  • A.直接修改所有数据并立即对外可见
  • B.对所有涉及的Region写入预写记录并加锁← 正确答案
  • C.把所有数据复制一份到备份节点
  • D.等待全局时钟校准后再写入

答案:B。Prepare阶段的任务是让所有涉及的Region在本地完成预写(写入未提交的改动)并打上锁标记,然后向协调者回复Ready。只有所有Region都Ready后才会进入Commit。A对应提交后的行为,C是备份机制,D不是2PC的组成部分。

Q8. 下列哪些属于TiDB计算层(TiDB Server)的职责?

多选题 · mid

  • A.解析SQL并生成执行计划← 正确答案
  • B.存储真实的数据行
  • C.通过Raft协议复制日志
  • D.将子任务下推给TiKV执行← 正确答案

答案:AD。TiDB Server负责SQL解析、计划生成与任务下推。真实数据行由TiKV存储,Raft日志复制发生在TiKV层面,因此B和C都不是TiDB Server的职责,选A和D。

Q9. 关于Region分裂,下列说法正确的有?

多选题 · mid

  • A.分裂后两个Region的key范围互补且不重叠← 正确答案
  • B.分裂过程会导致整个集群停止写入
  • C.分裂后Region副本会自动重新负载均衡← 正确答案
  • D.Region分裂由PD直接在元数据中标记即可

答案:AC。分裂后的左右两个Region交集为空、并集为原Region,正确;分裂完成后PD会把副本调度到其他节点做负载均衡,正确。分裂是逻辑切分,不会导致集群停写;且分裂需要TiKV实际执行切分,并非PD改元数据就能完成,因此B、D错误。

Q10. 关于TiDB分布式事务的性能与行为,下列说法正确的有?

多选题 · extreme

  • A.同Region内的key可走本地事务加速← 正确答案
  • B.跨Region事务必须经过两阶段提交← 正确答案
  • C.所有事务无论范围大小都强制两阶段提交
  • D.异步提交技术可降低跨Region事务延迟← 正确答案

答案:ABD。TiDB对单Region事务会自动走本地Raft提交路径,无需两阶段,因此A正确、C错误;跨Region事务必须2PC保证原子性,B正确;异步提交让Prepare与Commit的等待重叠,能有效降低跨Region延迟,D正确。