PCTP学习笔记-TiDB V6 数据库核心原理与架构(持续更新中)
本学习笔记由个人为备考 PCTP 认证整理而成,仅供个人学习之用,并非官方教材,内容仅代表个人理解。
PingCAP 官方拥有所有核心内容的知识产权。本笔记严禁用于任何商业目的。若官方认为笔记内容存在侵权问题,请告知,我将立即删除。
参考资料:
TiDB 数据库架构概述

该图表展示了 TiDB 分布式数据库的核心体系架构。TiDB 被设计为一个云原生的分布式数据库,其架构清晰地划分为三个核心组件集群,共同协作以提供高性能和高可用性。
核心特性
该架构的主要优点包括:
- 一键水平扩缩容
得益于 TiDB 存储计算分离的架构的设计,可按需对计算、存储分别进行在线扩容或者缩容,扩容或者缩容过程中对应用运维人员透明。
- 金融级高可用
数据采用多副本存储,数据副本通过 Multi-Raft 协议同步事务日志,多数派写入成功事务才能提交,确保数据强一致性且少数副本发生故障时不影响数据的可用性。可按需配置副本地理位置、副本数量等策略,满足不同容灾级别的要求。
- 实时 HTAP
提供行存储引擎 TiKV、列存储引擎 TiFlash 两款存储引擎,TiFlash 通过 Multi-Raft Learner 协议实时从 TiKV 复制数据,确保行存储引擎 TiKV 和列存储引擎 TiFlash 之间的数据强一致。TiKV、TiFlash 可按需部署在不同的机器,解决 HTAP 资源隔离的问题。
- 云原生的分布式数据库
专为云而设计的分布式数据库,通过 TiDB Operator 可在公有云、私有云、混合云中实现部署工具化、自动化。
- 兼容 MySQL 协议和 MySQL 生态
兼容 MySQL 协议、MySQL 常用的功能、MySQL 生态,应用无需或者修改少量代码即可从 MySQL 迁移到 TiDB。提供丰富的数据迁移工具帮助应用便捷完成数据迁移。
三大集群组件
TiDB Cluster
-
作用:TiDB Server 是用户与 TiDB 数据库交互的入口,它是一个 无状态 的组件,负责处理所有来自客户端的 SQL 请求。TiDB 数据库的计算
-
处理客户端的连接:作为用户连接数据库的接口,它负责管理来自应用程序的所有连接。
-
SQL 语句的解析和编译:当收到 SQL 语句后,TiDB Server 会对其进行解析,生成抽象语法树(AST),并编译成可执行的物理执行计划。
-
关系型数据与 KV 的转化:TiDB Server 的一个核心功能是将关系型数据模型(rows、columns)映射到底层的键值对(KV)存储模型 。它负责将 SQL 查询转化为对 TiKV 存储层的 KV 请求。
-
SQL 语句的执行:它根据生成的执行计划,向 TiKV 存储集群发出读写请求,并处理返回结果。
-
执行 online DDL:TiDB Server 支持在不阻塞在线服务的情况下执行数据定义语言(DDL)操作,如添加列、创建索引等。
-
垃圾回收(GC):TiDB 采用多版本并发控制(MVCC)机制。垃圾回收是其重要的后台任务,负责清理不再需要的旧版本数据,以保持系统性能和存储效率。
-
-
架构:图中的
TiDB Server位于左侧,与应用程序直接交互。从图表可以看出,多个 TiDB Server 实例可以并行运行。由于它们不存储任何持久化数据,因此每个实例都是无状态的。这种设计带来了巨大的优势:-
弹性扩展:当并发请求增加时,可以轻松地增加 TiDB Server 实例来水平扩展计算能力,而无需担心数据迁移。
-
高可用:即使某个 TiDB Server 实例发生故障,其他实例也可以立即接管其工作,确保服务不受影响。
-
PD (Placement Driver) Cluster
-
作用:是整个集群的“大脑”,负责管理集群的元数据和全局协调。推荐结合 TiDB 数据库的调度 学习。
-
整个集群的元数据存储:PD 存储了集群中所有 Region(数据块)的元数据信息,包括每个 Region 的位置和副本分布情况。这使得 PD 能够全面掌握集群的实时状态。
-
分配全局 ID 和事务 ID :PD 充当着全局 ID 和事务 ID 的分配中心。它为每个事务分配一个唯一的事务 ID,确保了事务的 ACID 特性,特别是在处理并发操作时,能够保证数据的正确性和一致性。
-
生成全局时间戳 TSO:TSO 是 TiDB 实现分布式事务的关键,它为每个事务分配一个全局单调递增的时间戳,以确保事务在全集群范围内的顺序和一致性。
-
收集集群信息进行调度:PD 会持续从 TiKV 和 TiFlash 存储节点接收心跳信息,以收集节点的负载、存储空间和网络流量等实时状态。PD 利用这些信息来做出智能的调度决策。
-
提供 TiDB Dashboard 服务:PD 集群集成了 TiDB Dashboard,这是一个可视化的监控和管理工具,可以帮助运维人员直观地查看集群的各项指标和运行状态。
-
-
架构:图中的
PD cluster位于顶部,由至少 3 个PD节点构成,拥有高可用的能力。建议部署奇数个PD 节点。-
心跳 (Heartbeat):Storage Cluster(包括 TiKV 和 TiFlash 节点)会定期向 PD Cluster 发送心跳包,报告自身的状态。
-
调度操作:PD 根据心跳信息和其内部的调度策略,向 Storage Cluster 发出调度指令。例如,当某个 TiKV 节点的负载过高时,PD 会将它上面的一些 Region 迁移到其他负载较低的节点。
-
时间戳服务 (TSO):TiDB Server 在发起新事务时,会向 PD 请求一个全局唯一的 TSO。PD 将 TSO 返回给 TiDB Server,后者用其来保证事务的原子性和隔离性。
-
Storage Cluster
-
作用:负责数据的实际存储。这是 TiDB 的存储层,它将数据以键值对的形式存储在
TiKV和TiFlash中。推荐结合 TiDB 数据库的存储 学习。 -
架构:
-
TiKV:是一个分布式的行存引擎,提供高并发和强一致性。它负责处理在线事务(
OLTP)。图中的蓝色块就是TiKV节点,它们之间通过同步复制来保证数据安全。-
特性:
-
数据持久化:TiKV 使用 RocksDB 作为底层存储引擎,确保所有写入的数据都能被持久化到磁盘上,即使系统重启也不会丢失。
-
副本的强一致性和高可用性:TiKV 采用 Raft 共识算法来保证数据副本之间的一致性。数据在写入时,必须得到大多数副本的确认才能被视为成功。这确保了数据的强一致性。同时,当主节点故障时,Raft 能够自动选举出新的主节点,从而实现高可用性。
-
MVCC (多版本并发控制):TiKV 实现了 MVCC 机制,允许读写事务在不互相阻塞的情况下并发执行。对于每次写入操作,TiKV 都会创建一个新的版本,而读操作可以读取旧版本的数据,这大大提高了并发性能。
-
分布式事务支持:TiKV 支持大规模的分布式事务,这意味着它可以保证跨越多个节点的事务的 ACID (原子性、一致性、隔离性、持久性) 特性。
-
Coprocessor (算子下推):TiKV 的 Coprocessor 功能允许将部分计算任务(如过滤、聚合)从 TiDB Server 下推到 TiKV 存储节点。这减少了网络传输的数据量,利用了 TiKV 节点的计算能力,从而显著提高了查询效率。
-
-
内部架构
每个 TiKV 节点都包含以下关键组件:
-
Transaction (事务):负责处理事务的逻辑,协调 MVCC 机制。
-
MVCC:多版本并发控制层,管理数据的不同版本。
-
Raft:共识算法层,负责确保数据副本之间的一致性和主节点的选举。
-
RocksDB:底层的键值存储引擎,真正负责将数据持久化到磁盘上。在 TiKV 中,RocksDB 被分为两个实例:一个用于存储 Raft 日志,另一个用于存储实际的 KV 数据。
-
-
-
TiFlash:是一个分布式的列存引擎,用于加速在线分析处理(
OLAP)。它从TiKV同步数据,提供快速的分析查询能力。图中的绿色块就是TiFlash节点。-
特性:
-
异步复制:TiFlash 中的副本以特殊角色 (Raft Learner) 进行异步的数据复制。这表示当 TiFlash 节点宕机或者网络高延迟等状况发生时,TiKV 的业务仍然能确保正常进行。
-
一致性:尽管采用异步复制,TiFlash 仍然能够提供读取一致性。TiFlash 提供与 TiKV 一样的快照隔离支持,且保证读取数据最新(确保之前写入的数据能被读取)。这个一致性是通过对数据进行复制进度校验做到的。每次收到读取请求,TiFlash 中的 Region 副本会向 Leader 副本发起进度校对(一个非常轻的 RPC 请求),只有当进度确保至少所包含读取请求时间戳所覆盖的数据之后才响应读取。
-
列式存储提高分析查询效率:与 TiKV 的行式存储不同,TiFlash 使用列式存储(Columnar Storage)。在分析查询中,通常只需要访问少量列。列式存储可以只读取所需的列,大大减少了 I/O 开销,从而显著提高查询效率。
-
业务隔离:TiFlash 和 TiKV 是独立的存储引擎。这使得在线事务处理(OLTP)和在线分析处理(OLAP)可以在各自的节点上运行,互不干扰。这种计算和存储隔离的设计避免了分析查询对在线业务性能的影响。
-
智能选择:当 TiDB Server 接收到查询请求时,其优化器会根据查询的类型和成本,智能地选择是使用 TiKV(适合事务型查询)还是 TiFlash(适合分析型查询)来执行,从而自动获得最佳性能。
-
-
-
总的来说,TiDB 将计算(TiDB Server)、调度(PD)和存储(Storage Cluster)三个组件解耦,形成一个高度弹性、可扩展和高可用的分布式架构。
TiDB Server
TiDB Server 是用户与 TiDB 数据库交互的入口,它是一个 无状态 的组件,负责处理所有来自客户端的 SQL 请求。

特性详解
先回顾一下TiDB Server的功能特性,然后按照功能进行拆解。
-
处理客户端的连接
-
SQL 语句的解析和编译
-
关系型数据与 KV 的转化
-
SQL 语句的执行
-
执行 online DDL
-
垃圾回收(GC)
1. 处理客户端的连接
- 对应模块:
Protocol Layer(协议层) - 解析:这是 TiDB Server 的最外层,它负责接收和处理来自客户端的所有网络连接,按照 MySQL 协议实现,使得任何支持该协议的应用程序都能无缝连接到 TiDB。
Protocol Layer工作流程

-
接收数据包(
Packet)Client(客户端)通过网络发送数据包到 TiDB。Listener模块是协议层的第一个组件,它监听来自客户端的连接请求,并接收这些数据包。
-
管理连接上下文(
Connection Context)Listener接收到数据包后,会将其传递给Connection Context。Connection Context负责为每个客户端连接维护其状态和上下文信息,例如会话变量、用户权限等。这使得 TiDB 能够管理多个并发连接。
-
协议解码(
Protocol Decode)Connection Context将数据包中的命令(Command)传递给Protocol Decode模块。Protocol Decode的核心任务是解析客户端发送的数据包,将其从网络协议格式(例如 MySQL 协议)解码成 TiDB Server 内部能够理解的命令和数据。例如,它会识别出这是一个SELECT语句还是一个UPDATE语句。
-
命令执行
- 解码后的命令(
Command)随后会被发送到 TiDB Server 的其他核心模块(例如解析器、执行器)进行处理。
- 解码后的命令(
-
协议编码(
Protocol Encode)- 在 TiDB Server 完成命令执行并生成数据(
Data)后,Protocol Encode模块负责将这些内部数据编码成客户端能够理解的网络协议格式。 - 编码后的数据包(
Packet)随后会被发送回客户端。
- 在 TiDB Server 完成命令执行并生成数据(
-
核心作用总结
- 隔离:协议层将复杂的网络通信细节与 TiDB Server 的核心处理逻辑隔离开来。
- 兼容性:它通过编解码模块,实现了对 MySQL 协议的无缝兼容,使得 TiDB 能够利用 MySQL 的生态系统。
- 并发:
Connection Context模块能够同时处理和管理多个客户端连接,确保了 TiDB Server 的高并发能力。
2. SQL 语句的解析和编译
- 对应模块:
Parse和Compile - 工作流:
Protocol Layer接收到 SQL 文本后,会将其交给Parse模块进行解析,生成抽象语法树(AST)。随后,Compile模块会根据AST,通过查询优化器生成一个高效的执行计划。

- 举例:
SELECT * from T where age > 19

-
1.TiDB Server 的 SQL 解释过程:词法分析 (Lexical Analysis)
- 输入:原始 SQL 语句
SELECT * from T where age > 19。 - 过程:
词法分析(lex)模块会将 SQL 语句分解成一系列有意义的最小单元,即Token。这些Token包括关键词、表名、列名、操作符和常量等。 - 输出:图中的第一步将 SQL 语句分解为六个
Token:select、列名、from、表名、where和条件。
- 输入:原始 SQL 语句
-
2.TiDB Server 的 SQL 解释过程:语法分析 (Syntactic Analysis)
- 输入:
词法分析生成的Token序列。 - 过程:
语法分析(yacc)模块会检查Token序列是否符合 SQL 语言的语法规则。它会根据语法规则,将Token序列组织成一个树状结构,称为抽象语法树(Abstract Syntax Tree, AST)。 - 输出:图中的第二步展示了生成的
AST。SELECT部分:select关键词作为根节点,下面连接着列名(name,age,address)和表名(T)。WHERE部分:where关键词作为根节点,下面连接着条件。条件节点进一步分解为操作符gt(>),以及其左右操作数age和19。
- 输入:

-
3.TiDB Server 的 SQL 编译过程:验证(
Validator)- 输入:
Parse模块生成的AST。 - 作用:此步骤会检查
AST的合法性。它会验证 SQL 语句中引用的表、列、函数是否存在,以及用户是否有足够的权限执行该操作。如果验证失败,编译过程将终止并返回错误。 - 数据来源:验证过程需要访问 TiDB Server 内部缓存的元数据(
memBuffer -> 元数据),以获取数据库的模式信息。
- 输入:
-
4.TiDB Server 的 SQL 编译过程:逻辑优化(
逻辑优化)- 作用:在验证成功后,优化器会开始进行逻辑优化。此步骤会重写
AST,将复杂或效率低下的操作转换为逻辑上等效但更高效的操作。 - 例如:优化器可能会改变
JOIN的顺序,或将WHERE子句中的条件进行简化,以减少后续需要处理的数据量。这个阶段只关注操作的逻辑等效性,不考虑具体的物理实现。
- 作用:在验证成功后,优化器会开始进行逻辑优化。此步骤会重写
-
5.TiDB Server 的 SQL 编译过程:物理优化(
物理优化)- 作用:此步骤将逻辑执行计划转换为一个或多个具体的物理执行计划。它会考虑 TiDB 存储集群的实际物理特性和数据分布情况,并根据成本模型选择最优的方案。
- 数据来源:
物理优化需要访问 TiDB Server 内部缓存的统计信息(memBuffer -> 统计信息),例如表的大小、索引的选择性等。它会计算不同执行计划的成本(例如 I/O 和 CPU 开销),并选择成本最低的计划。 - 输出:最终输出一个最优的执行计划,该计划可以直接在 TiDB Server 的执行器中执行。
3. 关系型数据与 KV 的转化
-
对应模块:
KV -
解析:
KV模块是 TiDB Server 的核心组件之一。它负责将 SQL 中的关系型数据模型(例如表、行和列)映射到底层的键值对(KV)存储模型,以便后续发送给 TiKV 存储层。
同时也包含以下接口:-
Transaction 接口:这是所有事务操作的入口。它包括提交(Commit)和回滚(Rollback)等方法,并能设置事务的隔离级别(IsolationLevel)和优先级(Priority)。
-
MemBuffer 接口:用于内存缓冲,代表了一个内存中的键值集合,用于在事务提交前缓冲所有的写入操作。这使得事务能够高效地在内存中进行修改,减少了对底层存储的直接访问。
-
读写接口:该包将数据操作分为了两个清晰的接口:
-
Retriever:负责所有读取操作,如 Get(获取)和 Seek(查找)。
-
Mutator:负责所有写入操作,如 Set(设置)和 Delete(删除)。
-
-
客户端交互:Client 和 Request 接口定义了 TiDB Server 与 TiKV 存储层之间的通信协议。Request 包含了请求的类型、时间戳和数据等信息,而 Client 则负责将这些请求发送给 TiKV。
-
数据快照:Snapshot 接口提供了获取特定时间点数据快照的能力,这是 TiDB 实现快照隔离(Snapshot Isolation)的关键,确保了读取操作的强一致性
-
-
TiDB 关系型数据与 KV 存储的转换
-
转换的核心思想
TiDB 为每个表、每行数据、每个索引都分配了一个唯一的
ID,并将这些ID与原始数据结合起来,形成唯一的键值对。这些 ID 都是 int64 类型。
-
为表分配
TableID:TiDB为每个表分配一个整个集群内唯一的TableID。 -
为行分配
RowID:TiDB会为表中每一行数据分配一个在表内唯一的RowID。如果表中存在整数型主键,TiDB会将主键的值作为RowID,从而节省空间并提高效率。每行数据按照如下规则编码成 (Key, Value) 键值对:
Key: tablePrefix{TableID}_recordPrefixSep{RowID} Value: [col1, col2, col3, col4] -
为索引分配
IndexID:TiDB会为表中每个索引分配一个在表内唯一的IndexID。对于主键和唯一索引,按照如下规则编码成 (Key, Value) 键值对:
Key: tablePrefix{tableID}_indexPrefixSep{indexID}_indexedColumnsValue Value: RowID对于不需要满足唯一性约束的普通二级索引,一个键值可能对应多行,需要根据键值范围查询对应的 RowID。因此,按照如下规则编码成 (Key, Value) 键值对:
Key: tablePrefix{TableID}_indexPrefixSep{IndexID}_indexedColumnsValue_{RowID} Value: null
-
Key-Value 映射关系示例
通过一个简单的例子,来理解 TiDB 的 Key-Value 映射关系。假设 TiDB 中有如下这个表:CREATE TABLE User ( ID int, Name varchar(20), Role varchar(20), Age int, PRIMARY KEY (ID), KEY idxAge (Age) );假设该表中有 3 行数据:
1, "TiDB", "SQL Layer", 10 2, "TiKV", "KV Engine", 20 3, "PD", "Manager", 30首先每行数据都会映射为一个 (Key, Value) 键值对,同时该表有一个 int 类型的主键,所以 RowID 的值即为该主键的值。假设该表的 TableID 为 10,则其存储在 TiKV 上的表数据为:
t10_r1 --> ["TiDB", "SQL Layer", 10] t10_r2 --> ["TiKV", "KV Engine", 20] t10_r3 --> ["PD", "Manager", 30]除了主键外,该表还有一个非唯一的普通二级索引 idxAge,假设这个索引的 IndexID 为 1,则其存储在 TiKV 上的索引数据为:
t10_i1_10_1 --> null t10_i1_20_2 --> null t10_i1_30_3 --> null -
转换流程
当
TiDB Server收到一个SQL语句后,它会执行以下转换:
- 关系型查询:用户发起一个关系型查询,例如
SELECT * FROM T WHERE name = "TiDB"。 - 定位
Key范围:TiDB Server的KV模块会将这个关系型查询转换为对底层Key-Value存储的键范围扫描(Key Range Scan)操作。 - 过滤与聚合:为了减少网络开销和提高效率,
TiDB Server会将过滤条件(例如name = "TiDB")下推到TiKV存储节点。 - 返回结果:
TiKV节点在本地完成过滤后,只将满足条件的行数据返回给TiDB Server,TiDB Server再将其组装成最终结果返回给客户端。
-
4. SQL 语句的执行
- 对应模块:
Executor、DistSQL,KV,TiKV Client,Transaction

-
解析:
Executor负责执行由Compile模块生成的查询计划。DistSQL(分布式 SQL)模块将复杂的SQL任务分发到底层的 TiKV/TiFlash 集群执行。-
执行计划给到Executor,Executor逐层执行;
-
对于复杂sql,比如范围查询、表连接等,为了避免复杂sql的执行和TiKv层耦合度太高,TiDB增加了 DistSQL 模块。DistSQL 模块位于 Executor 和 TiKV 之间,用于将复杂的SQL变成单表任务的组合发送给TiKV;
-
只有复杂SQL 走 DistSQL,对于点查,根据主键等索引进行等值查询走KV模块;
-
最终都会经过 TiKV Client,TiKV Client 主要负责向TiKV集群发送读写请求;
-
对于事务操作,会启用 Transaction 和 KV 模块,注意:事务的提交时间是由 Transaction 在提交完事务之后向PD获取的;
-
5. 执行 online DDL

-
对应模块:
worker和start job -
解析:当有 DDL 任务需要执行时,
start job模块接受用户语句,worker模块则在后台执行 DDL 操作,而不会阻塞在线服务。- 对于整个TiDB数据库来讲,同一时刻只能有一个TiDB server 做 DDL;
-
执行流程:
-
用户发出 DDL 语句,Start Job 模块接受请求;
-
Start Job 将 DDL 作为一个 Job 放在 TiKV 的任务队列(Job Queue)中;
-
有一个名为 Owner 的 TiDB server (Owner 是轮换当的) 负责从任务队列(Job Queue)中逐个取出Job 并执行,即使这个 Job 不是该TiDB server的;
-
执行过后将 Job 放入历史队列(History Queue);
-
-
注意:
-
Job Queue 是放在TiKV里面的,为了持久化,即使TiDB Server 挂掉,DDL任务依然存在。
-
关于 Schema load 模块, 用于 TiDB server 在当上Owner之后同步最新的表信息到内部缓存,便于执行Job。
6. 垃圾回收(GC)
-
对应模块:
GC -
解析:
GC模块是专门用于执行垃圾回收的组件。它负责定期清理 TiKV 存储层中,在多版本并发控制(MVCC)机制下产生的、不再需要的旧版本数据,从而释放存储空间并维护数据库的健康。-
每个 TiDB Server 都拥有一个GC模块。一个 TiDB Server会被选为 GC Leader,控制整个 GC 。
-
TiDB Server 会得到一个 SafePoint (时间戳),会标记所有时间戳早于 SafePoint 的旧版本数据。这些被标记的数据就是即将被回收的“垃圾”。
-
GC Life Time 默认为10分钟,GC Life Time内的历史数据会被保留。
-
7. TiDB Server 的 缓存
-
默认使用数据库的全部内存
-
缓存组成
-
SQL 结果:用于缓存最近执行的查询结果,当相同的查询再次到来时,可以直接返回结果而无需重新计算,从而显著提高查询速度。常见使用场景:大表JOIN、事务修改。
-
线程缓存:为每个工作线程维护独立的缓存,用于存储线程私有的数据,减少线程间同步开销,提高并发性能。
-
元数据、统计信息:缓存数据库的元数据(如表结构、索引信息)和查询优化所需的统计信息。这部分信息对于 SQL 查询的快速解析和优化至关重要。
-
-
缓存的管理
-
tidb_mem_quota_query:这个参数用于设置单个 SQL 查询的内存使用上限。当一个查询的内存使用超过这个限制时,它会被终止,以保护整个系统的稳定性。
-
oom-action:OOM(Out of Memory)action 定义了当 TiDB Server 遇到内存不足(OOM)的情况时应采取的行动,例如是终止查询还是记录警告。
-
-
热点小表缓存

-
热点小表缓存机制的触发条件
-
数据量不大:表的总数据量相对较小,64M以下。
-
只读或修改不频繁:表中的数据极少被写入或更新。
-
访问很频繁:表是应用程序中的热点数据,被频繁地读取。
-
-
工作原理
如图所示,TiDB Server 会将符合上述条件的整个表的数据从 TiKV 加载到 cache table 中。
- 缓存设置:开发者可以使用
ALTER TABLE ... CACHE语句将一个表设置为可缓存。例如:ALTER TABLE users CACHE; - 租约(Lease)机制:
TiDB Server的缓存机制基于一个租约(Lease)模型。tidb_table_cache_lease参数定义了缓存的有效期,其默认值为 5 秒 。租约有效期内,可以读缓存,但阻塞写。租约到期,数据过期,可以读写,但直接接触TiKV而非缓存。 - 数据加载:当 TiDB Server 首次接收到针对一个已缓存表的查询时,它会从底层的 TiKV 存储层将整个表的数据加载到其内存中的
cache table中。 - 高速查询:在租约有效期内(例如 5 秒),所有对该表的查询都将直接从 TiDB Server 的内存缓存中返回,完全绕过了 TiKV,从而极大地减少了网络延迟和 I/O 开销,实现了毫秒级的查询响应。
- 缓存设置:开发者可以使用
-
注意事项
-
大小限制:TiDB 对每张缓存表的大小限制为 64 MB 。这是为了防止单个大表占用过多内存资源。
-
写操作阻塞:在缓存的租约(lease)有效期内(由 tidb_table_cache_lease 参数控制),任何对该表的写操作都会被阻塞。
-
性能波动:当缓存租约到期时,读数据直接走TiKV,这可能导致该时间点的读性能下降。
-
DDL 限制:不支持对缓存表直接执行 DDL 操作。在进行 DDL 操作前,必须先关闭表的缓存功能。
-
性能调优:对于那些加载较慢或极少修改的表,可以适当延长 tidb_table_cache_lease 的时间,以减少缓存刷新的频率,从而保持读性能的稳定。
-
TiKV
TiKV-持久化

TiKV 的核心功能
-
数据持久化
- TiKV 使用 RocksDB 作为其底层的存储引擎。这确保了所有数据在写入后都能被可靠地持久化到磁盘上,即使系统重启也不会丢失。
-
分布式强一致性
- TiKV 采用 Raft 共识算法来管理数据副本。每个数据块(
Region)都有多个副本,通过 Raft 协议保证数据在所有副本之间始终保持一致。这确保了数据的正确性。
- TiKV 采用 Raft 共识算法来管理数据副本。每个数据块(
-
多版本并发控制(
MVCC)- TiKV 实现了
MVCC机制,允许读写事务并发执行,而不会互相阻塞。当数据被修改时,TiKV 会创建一个新版本,旧版本的数据仍然可供读取。这大大提高了高并发场景下的性能。
- TiKV 实现了
-
分布式事务
- TiKV 能够支持跨越多个节点的分布式事务。它通过与 PD(Placement Driver)集群协作,确保了这些事务的原子性、一致性、隔离性和持久性(
ACID),保证了数据在整个分布式系统中的完整性。
- TiKV 能够支持跨越多个节点的分布式事务。它通过与 PD(Placement Driver)集群协作,确保了这些事务的原子性、一致性、隔离性和持久性(
-
Coprocessor (算子下推)
- TiKV 的
Coprocessor功能允许将部分计算任务(如过滤、聚合)直接从计算层(TiDB Server)下推到 TiKV 执行。这减少了 TiDB Server 和 TiKV 之间的数据传输量,利用了 TiKV 节点的计算资源,从而显著提高了查询效率。
- TiKV 的
TiKV 的数据持久化
RocksDB 特性解析
RocksDB 是一个专为高性能存储而设计的嵌入式键值(Key-Value)数据库,尤其在 TiDB 的 TiKV 存储引擎中扮演着关键角色。
核心优势
-
针对
Flash存储优化:RocksDB 专为固态硬盘(Flash存储)进行了深度优化,能够实现极低的延迟。它采用了 LSM(Log-Structured Merge-tree)存储引擎,将写入操作顺序地追加到日志文件中,从而最大化了Flash存储的写入性能和寿命。 -
高性能的键值数据库:RocksDB 是一个嵌入式数据库,可以直接集成到应用程序中。它能够提供极高的读写性能,是许多需要高速键值操作的分布式系统(如 TiKV)的首选。
-
完善的持久化机制:RocksDB 提供了健壮的持久化机制,能够在保证数据安全性的同时,维持高性能的读写能力。
-
良好的支持范围查询:除了基本的键值查找,RocksDB 还支持范围查询(
range query),这对于处理类似 SQL 的查询非常重要。 -
设计用途:RocksDB 是为那些需要将 TB 级别数据存储在本地
FLASH或RAM中的应用程序服务器而设计的。它完美适配了需要高速存储的中小型键值对,可以直接存储在FLASH或内存中。 -
多核友好:RocksDB 的性能能够随
CPU核心数量线性提升,这使得它在多核系统中表现出色。
总的来说,RocksDB 是一个为现代高速存储设备而生的高性能、低延迟的键值数据库,它的设计理念和技术特点使其成为 TiDB 等分布式数据库的理想底层存储引擎。
RocksDB 写入流程

RocksDB 的写入流程基于 LSM(Log-Structured Merge-tree) 存储引擎的核心思想。
1. 内存中的写入(Memory)
-
日志先行(WAL):为了保证数据持久化,在数据写入
MemTable的之前,会写入到磁盘上的日志先行(WAL, Write-Ahead Log)文件中。WAL是一种顺序写入的日志,它能够在系统崩溃时用于恢复MemTable中的数据。
在 sync_log = TRUE 的情况下,写入日志时会直接调用操作系统的 fsync 命令。这个操作的目的是绕过操作系统的文件系统缓存,强制将内存中的数据立即同步到磁盘上。这样做可以防止在系统断电或崩溃时,数据因仍停留在缓存中而丢失。 -
内存写入:WAL 操作完毕之后,数据被写入到内存中的一个可变内存表(
MemTable)。 -
转为不可变:当
MemTable达到一定大小时(write_buffer_size),它会转存为不可变内存表(immutable MemTable)。这时,一个新的可变MemTable被创建,用于接收新的写入。immutable MemTable用于防止写阻塞。
2. 刷新到磁盘(Flush)
- 刷新管道:不可变内存表(
immutable MemTable)会进入一个刷新管道(flush pipeline)。 - 生成
SST文件:在刷新管道中,这些不可变内存表的数据会以批量、有序的方式被刷新(flush)到磁盘上,形成一个或多个有序字符串表(SST, Sorted String Table)文件。
如果immutable MemTable写入太快了,超过了5个,默认会触发 RocksDB 的流控(Write Stall),会使写入速度变慢。 - 垃圾回收:一旦
MemTable的数据成功刷新到SST文件,并且WAL中对应的日志不再需要,WAL文件就可以被安全地删除或回收。
RocksDB 的分层存储与合并压缩

Level 0 层
immutable MemTable 直接刷到磁盘之后,形成 Level 0 的一个 SST(Sorted String Table)文件。
特点: Level 0 的文件是无序的,因为它们是直接从内存写入的,键的范围可能重叠。这就是为什么 Level 0 的文件数量(默认达到 4 个)是触发 Compaction 的关键。
从 Level 0 到 Level 1 的合并(Compaction)
当 Level 0 的文件数量达到阈值时,RocksDB 就会启动 Compaction。
它会选择 Level 0 的所有文件,与 Level 1 层中键范围重叠的文件进行合并。
这个过程会合并旧数据和新数据,删除被标记为已删除的记录,并按照键的顺序排序,最终生成新的、有序的 SST 文件,存放到 Level 1 层。
层层合并(Compaction)
每一层(除了 Level 0)都有一个预设的大小限制。
当某一层的文件大小总和超过阈值时,就会触发该层与下一层的 Compaction。
这个过程像一个瀑布,数据从上层流向下一层,一层层地合并、压缩和清理,最终到达最底层。
删除操作
在 LSM-tree 存储引擎中,由于数据是只追加的,你不能直接在原地修改或删除数据。所有删除和更新操作都被转化为一种特殊的写入。
删除一个键值对时,LSM-tree 不会立即从磁盘上移除它。相反,它会写入一个特殊的墓碑标记(Tombstone)。
-
写入过程: 当你执行 DELETE key 时,LSM-tree 会在 MemTable 中为该键写入一个特殊的记录。这个记录包含键本身,但值是一个空值,并带有一个删除标记。
-
内存与磁盘: 这个墓碑标记会像任何其他数据一样,从 MemTable 刷新到 Level 0,然后通过 Compaction 逐层下沉。
-
垃圾回收: 当 Compaction 发生时,如果它遇到了一个墓碑标记,就会连同该键的所有旧版本数据一起物理删除。这个旧数据和墓碑本身都会被清理掉,从而释放存储空间。
简单来说,删除不是真正的删除,而是写入一个删除标记,然后让后台的 Compaction 进程去清理。
更新操作
更新一个键值对时,LSM-tree 同样不会原地修改。它会写入一个新版本的数据。
-
写入过程: 当你执行 UPDATE key = 'new_value' 时,LSM-tree 会将这个新版本的数据(包含 key 和 new_value)写入到 MemTable。这个新记录拥有一个更高的版本号(时间戳)。
-
多版本共存: 在新版本被写入后的一段时间里,旧版本的数据仍然存在于磁盘的其他层中。
-
后台清理: 当后台的 Compaction 进程运行,发现同一个键有多个版本时,它会保留最新的一个版本,并物理删除所有旧版本,最终达到清理存储空间的目的。
简单来说,更新不是原地修改,而是追加一个新版本的数据,并依赖后台的 Compaction 来清理旧版本。
RocksDB 查询操作

-
相较于B+树较慢。
-
RocksDb每次写入,最新的数据永远在老数据上面。
-
Bloom Filter:每一个文件都有一个布隆过滤器,可以帮助判断集合中的元素是否不在当前文件里。
RocksDB:列簇(Column Families)

-
列簇是一种数据分片技术,将键值对根据不同的属性分配给不同的列簇,一个列簇的SST文件存一类键值对。即列簇允许你在一个 RocksDB 实例中,创建多个逻辑上相互独立的数据分区。
-
写入的时候可以指定列簇。不指定时有默认的Default列簇。
-
每个列簇都有自己的:
- 内存结构: 独立的
MemTable、immutable MemTable和Block Cache。 - 磁盘文件: 独立的 Level 0 到 Level N 的 SST 文件层级。
- 内存结构: 独立的
但是注意* WAL(Write-Ahead Log): 并不是独立的。
图片中清楚地展示了这一点:左右两个虚线框代表两个不同的列簇。它们各自拥有自己独立的内存和磁盘结构,但共享 WAL 文件。
TiKV 分布式事务
什么是分布式事务
分布式事务是指一个操作涉及到多个独立的数据存储或计算节点。为了确保数据的一致性,这个操作必须要么在所有节点上都成功,要么在所有节点上都失败。
TiDB 的分布式事务模型主要借鉴了 Google 的 Percolator 模型。
该模型是一种基于乐观并发控制的两阶段提交(2PC)协议。TiDB 在实现其分布式事务时,采用了 Percolator 模型的核心思想,具体体现在:
- 时间戳预言机(TSO):TiDB 使用 Placement Driver (PD) 作为时间戳预言机,提供全局唯一且单调递增的时间戳(
TSO),这与 Percolator 模型中的Timestamp Oracle角色一致。 - 两阶段提交:事务的执行分为预写(
prewrite)和提交(commit)两个阶段。 - MVCC:通过
TSO,TiDB 实现了多版本并发控制(MVCC),使得读写事务可以并发执行而不会互相阻塞。
TiDB 的分布式事务:单节点
TiDB 的分布式事务基于悲观事务模式和两阶段提交。它通过 start timestamp 和 commit timestamp 来实现 MVCC,从而在高并发下保证了数据的强一致性。

1. 事务的开始 (Begin)
- 事务启动:当客户端发起一个事务时,
TiDB Server会向PD(Placement Driver)请求一个全局唯一的开始时间戳(start timestamp)。图中的begin: start timestamp = 100表示这个事务的开始时间戳是100。 - 数据准备:在事务内部,所有写入或删除操作(
put、delete)首先会保存在 TiDB Server 的内存缓冲区中,不会直接写入TiKV。
2. 预写阶段 (Prewrite)
- 数据写入
Write列:在预写阶段,TiDB Server 会将事务中的数据写入TiKV节点。但此时写入的并不是最终数据,而是包含了开始时间戳的信息,并带有锁(Lock)信息。- 数据结构:TiKV会用三个列簇保存数据:
Default,Lock,Write。Default列簇:用于保存被修改的数据。图中的put <3_100, Frank>表示在Default列族中,键<3_xxx>被写入了值Frank,xxx是事务开始时间戳100。Lock列簇:用于保存锁信息。TiKV会在Lock列族中创建一个锁记录,包含了事务的开始时间戳、主键信息等。
在整个分布式事务模型中,只给整个事务的第一行数据加一把主锁,其余修改的行的锁都依附于该锁。
图中的<3,(W,pk,3,100...)>代表图中事务的锁信息。Write列簇:对应提交信息。
- 数据结构:TiKV会用三个列簇保存数据:
- 目的:预写阶段的作用是验证数据,确保事务要修改的键没有被其他事务锁定,并且没有发生冲突。如果预写成功,数据就处于一个“准备好”提交的状态。
3. 提交阶段 (Commit)
- 获取提交时间戳:如果所有预写都成功,
TiDB Server会再次向PD请求一个全局唯一的提交时间戳(commit timestamp)。图中的commit: commit timestamp = 110表示提交时间戳是110。 - 更新
Write列簇:TiDB Server再次向TiKV发送指令,将提交时间戳写入到数据的Write列族中。图中的put <3_110, 100>表示键<3_xxx>对应的写入操作的提交时间戳是110。 - 清理锁:
TiKV会根据这个提交时间戳,将Lock列簇中对应的锁记录清除,即 向Lock列簇 中新增一个删除状态的锁。如图中<3,(D,pk,3,100...)>,D 表示该锁已被删除。 - 目的:提交阶段的作用是原子性地完成事务。一旦提交时间戳被成功写入,这个事务的更改就是对所有其他事务可见的。
从读数据的角度理解
-
如果想读取 id=3 的数据,首先到 write 列簇读最近一次的修改时间,读到了
put <3_110, 100>,之后拿着id_最新修改时间戳(3_100)到Default列簇中查询数据。 -
如果在 write 列簇中没有找到最新修改时间,且在
Lock列簇中读到一把对应数据的锁,则代表该数据不可读。
注意事项
-
Write列族:当用户写入一行数据时,如果这行数据的长度(Value值)小于 255 字节,那么数据本身会被存储在Write列族中。即此时既有提交信息,又有修改信息。 -
Default列族:当用户写入的数据长度(Value值)超过 255 字节时,这行数据就会被存入Default列族中。
TiDB 分布式事务流程:多节点
这里默认数据全部写入default列簇

该图表详细展示了 TiDB 分布式事务在两个 TiKV 节点上如何通过两阶段提交协议协同工作。这扩展了之前单节点事务的例子,更清晰地说明了分布式事务如何保证数据的一致性。
1. 事务开始 (Begin)
- 事务启动:一个事务开始,其全局唯一的开始时间戳为
100。 - 操作:该事务要写入两条数据:
put <1, Tom> -> <1, Jack>和put <2, Andy> -> <2, Candy>,注意是只插入新值。 - 数据分布:这两条数据被分发到两个不同的
TiKV节点上:put <1, Tom> -> <1, Jack>发生在TiKV Node 1。put <2, Andy> -> <2, Candy>发生在TiKV Node 2。
2. 预写阶段 (PreWrite)
TiDB Server 向所有涉及的 TiKV 节点(Node 1 和 Node 2)发送预写请求。这是两阶段提交的第一阶段。
TiKV Node 1的操作:- 在
Default列族中写入数据put <1_100, Jack>。 - 在
Lock列族中写入一个锁记录,包含start timestamp(100) 和锁类型:写锁(W),主锁 (pk)。(因为之前讲过,在整个分布式事务模型中,只给整个事务的第一行数据加一把主锁)
- 在
TiKV Node 2的操作:- 在
Default列族中写入数据put <2_100, Candy>。 - 在
Lock列族中写入一个锁记录,包含start timestamp、 锁类型:写锁(W)和锁的指向 (@1),这里的锁就不是主锁了,存在与否取决于指向的锁。
- 在
这个阶段,两个节点都验证了没有其他事务正在锁定这些键。如果都验证成功,TiDB Server 就会进入下一阶段。
3. 提交阶段 (Commit)
在所有节点都成功完成预写后,TiDB Server 会向 PD 请求一个全局唯一的提交时间戳,这里是 110。这是两阶段提交的第二阶段。
TiKV Node 1的操作:TiDB Server指示Node 1将提交时间戳110写入Write列族中。Node 1会将Lock列族中对应的锁记录移除。
TiKV Node 2的操作:TiDB Server指示Node 2将提交时间戳110写入Write列族中。Node 2会将Lock列族中对应的锁记录移除。
4.原子性解决:提交阶段的自动恢复
- 节点宕机:在提交阶段,
TiDB Server向Node 1和Node 2发送提交请求。如果Node 2在收到请求前或处理请求时宕机,而Node 1成功提交了事务(即写入了commit timestamp并删除了pk锁)。 - 新请求到来:当
Node 2恢复后,如果有新的请求需要访问被该事务修改的数据(例如,一个select请求)。 - 清理和提交:在处理新请求前,
TiKV会去检查相关键的锁记录。- 它会在
Lock列簇中发现一个未被清理的锁。 - 这个锁指向该事务的主键(
primary key),该主键位于Node 1上。 TiKV会向Node 1查询该主键的状态。由于Node 1已经成功提交了事务,TiKV会发现该事务已经有了提交时间戳(commit timestamp)。Node 2意识到这是一个已提交但未清理的事务,因此它会补充执行提交步骤,即写入自己的commit timestamp并清理锁。
- 它会在
提交阶段的自动恢复确保了即使部分节点失败,事务的最终状态仍是提交或回滚,从而保证了分布式事务的原子性和数据的一致性。
MVCC
MVCC(多版本并发控制)是一种并发控制技术,它的主要思想是为每一行数据保留多个版本,而不是通过加锁来阻塞读写操作。
MVCC 的核心工作原理
MVCC 的实现就像数据库为每一行数据拍了一张“快照”。当有新的写入操作时,它不会直接修改原始数据,而是创建一个新的数据版本。
- 写操作:当一个事务要修改一行数据时,它不会加锁来阻止其他事务读取。相反,它会创建一个新的数据版本,并给这个新版本打上一个唯一的、单调递增的时间戳。这个时间戳通常结合事务的 ID。
- 读操作:当一个事务要读取一行数据时,它只会看到在它开始时间点之前就已提交的数据版本。也就是说,它看到的是一个“快照”,不会被正在进行的写事务影响。
带来的好处
- 读不阻塞写,写不阻塞读:这是 MVCC 最大的优势。因为读事务和写事务操作的是不同的数据版本,它们之间不会互相等待,从而大大提高了数据库的并发性能。
- 快照隔离:每个读事务都能在一个固定的、不受其他事务影响的数据快照上工作,这保证了事务之间的高度隔离性。
TiKV 中 MVCC的实现
举例,有如下两个事务:
事务1
BEGIN;
-- 假设原始数据是 <1, Tom> 和 <2, Andy>
-- 事务 1 启动,获取 start_ts = 100
UPDATE your_table
SET name = 'Jack'
WHERE id = 1;
UPDATE your_table
SET name = 'Candy'
WHERE id = 2;
-- 事务 1 提交,获取 commit_ts = 110
COMMIT;
事务2(未提交)
BEGIN;
-- 事务 2 启动,获取 start_ts = 115
UPDATE your_table
SET name = 'Tim'
WHERE id = 1;
UPDATE your_table
SET name = 'Jerry'
WHERE id = 4;
- 事务的存储状况:

-
MVCC的实现:写不阻塞读
- 在 TSO = 120 时读取id = 1的值
-
首先在 Write 中查找最近提交的记录,put <1_110, 100>,获取到最新提交时间戳为110。注意 Write 里面的排列顺序,从下往上看业务ID由小到大、时间戳由大到小。
-
使用 1_100 到default里查找对应的数据。
- 在 TSO = 120 时写入id = 1
-
首先在 Write 中查找最近提交的记录,put <1_110, 100>,获取到最新提交时间戳为110。
-
到Lock里面查找锁,找到id = 1 对应一把未提交的锁,证明该数据不可被写。
TiKV-Raft
TiKV 中数据以 Region 为单位进行存储,且默认三个副本。 Region 是一个逻辑概念,里面存储一组按照Key排序的 Key—Value,默认最大96M。
TiKV 通过 Raft 保证了数据的强一致和高可用,而 Multi-Raft 机制则在此基础上实现了集群的高效负载均衡和水平扩展。

Raft Group(Raft 组)
Raft Group 是 TiKV 实现数据强一致性和高可用性的基础单元。它由一组 TiKV 节点组成,共同管理一个数据块(Region)的多个副本。
核心角色与职责
在一个 Raft Group 中,节点扮演着以下三种角色:
-
Leader(领导者):- 每个
Raft Group只有一个Leader,它是所有读写请求的唯一入口。 Leader负责将所有写入操作的日志(log)同步给Follower节点。- 它还会定期向
Follower发送心跳包,以证明自己仍然活跃。
- 每个
-
Follower(跟随者):Follower负责接收Leader的心跳和写入日志,并复制这些日志。- 如果
Follower在一段时间内没有收到Leader的心跳,它会认为Leader可能已失效,并自动成为候选者(Candidate),发起新的选举。
-
Candidate(候选者):- 当
Follower变为Candidate后,它会向其他节点请求投票,争取成为新的Leader。
- 当
一致性与写入过程
Raft Group 通过 Raft 协议确保了数据在所有副本间的强一致性:
- 写入请求:所有的写请求都必须首先提交给
Leader。 - 日志同步:
Leader将写入操作作为日志项(log entry)发送给所有Follower。 - 多数确认:一个写入操作只有在被大多数副本(
Leader自身加上多数Follower)确认后,才会被视为成功。 - 数据应用:一旦日志被确认,
Leader就会应用这个写入,并通知Follower也进行应用。
这个“多数派”原则是 Raft 协议的核心,它确保了即使少数节点发生故障,数据仍然是完整和一致的。
此外,当写入一个 Region(数据块)默认达到 96M 时,TiKV 会生成一个新的 Region继续写入。当一个 Region 达到 144MB 时,TiKV 会触发分裂。当 Region 变得太小,TiKV 会考虑合并。
Multi-Raft(多 Raft)
- 定义:
Multi-Raft机制是指在 TiKV 集群中,每个节点都可以同时管理多个Raft Group,并且扮演不同的角色。 - 图示:
TiKV node 1:它是Region 1的leader,同时是Region 2的follower。TiKV node 2:它是Region 1的follower,同时是Region 2的leader。TiKV node 3:它是Region 1和Region 2的follower。
- 核心优势:
Multi-Raft机制带来了巨大的优势:- 负载均衡:通过让不同的
Region在不同的节点上担任leader,可以有效地分散写入压力,避免单个节点成为瓶颈。 - 细粒度管理:
Raft Group的管理单位是Region(数据块),而不是整个节点。这使得 TiDB 可以对数据进行更细粒度的调度和管理。 - 可扩展性:当数据量增加时,可以创建新的
Region和Raft Group,并将其自动分配到集群中的各个节点,实现弹性扩展。
- 负载均衡:通过让不同的
Raft日志复制

该图表详细展示了 TiKV 存储引擎中 Raft 日志复制的流程。这是 TiDB 确保数据在多个副本之间保持强一致性的核心机制。
核心概念
Region与Raft Group:图中的Region4和Region5是两个独立的数据块,它们分别由不同的Raft Group进行管理。每个Raft Group都由 3 个副本组成,这些副本分布在不同的TiKV节点上。Leader与Follower:每个Raft Group有一个Leader,负责接收所有写入请求。其他节点是Follower,负责复制Leader的日志。- 在
Region4的Raft Group中,TiKV node 1是Leader。 - 在
Region5的Raft Group中,TiKV node 2是Leader。
- 在
- 日志(
log):每个写入操作都被视为一个日志条目(log entry)。日志条目包含操作类型(PUT,DEL)、键(key)、值(value)等信息,并带有唯一的日志索引。
日志复制流程

在整个TiKV中,存在两个RocksDB,一个用来存储键值(Key-Value)数据,一个用来存储Raft 日志(Raft log)。这种双 RocksDB 的设计实现了读写分离和数据隔离。
以 Region4 为例,日志复制的流程如下:
-
Propose(写入请求):所有的写入请求(例如
PUT key=1, name=tom)都必须先发送到Region4的Leader副本(位于TiKV node 1),Leader接收到一个 Raft日志。 -
Append (日志生成):
Leader会将这个 Raft日志 附加到本地(RocksDB_Raft)的日志中。日志标识组成:RegionId_logId -
Relipcate(日志同步):
Leader会将这个日志条目并行地发送给所有Region4的Follower副本(例如TiKV node 2和TiKV node 3)。Follower会将这个 Raft日志 附加到本地( RocksDB_Raft)的日志。 -
Committed(多数派确认):
Follower成功持久化日志后,给Leader返回一个响应值。只有当这个日志条目被大多数副本(超过一半以上的节点)确认后,Leader才会认为成功。注意这里的 Committed 状态意味着日志已经被大多数副本确认,可以被安全地应用到状态机(state machine)中,而不是数据的 Committed。 -
Apply(应用日志):一旦 Committed 成功,
Leader会将日志中的操作写入RocksDB KV中,并通知Follower也进行同样的操作,从而使所有副本的状态保持一致。
通过这种“多数派”的日志复制机制,Raft 协议确保了即使在少数节点宕机的情况下,数据仍然能够保持强一致性和高可用性。
Raft Leader选举
核心概念
-
Term:Term(任期)是Raft中的一个逻辑时间单位。每个Term都由一个唯一的、单调递增的整数标识。在每个Term中,最多只能有一个Leader。 -
Leader:负责所有读写请求的节点。 -
Follower:被动接收Leader日志的节点。 -
Candidate:在Leader选举过程中,Follower可能会转变为Candidate,主动发起选举。 -
Election Timeout:选举超时(Election Timeout)是一个非常重要的时间参数,它决定了 Raft Group 何时会触发新的 Leader 选举。-
Follower 的计时器:每个 Follower 节点内部都有一个选举计时器。只要 Follower 能够持续收到来自 Leader 的心跳或日志复制请求,它就会重置这个计时器。
-
触发选举:如果在选举计时器超时后,Follower 仍然没有收到任何来自 Leader 的消息,它就会认为 Leader 可能已经失效。此时,Follower 会采取以下行动:
-
增加自己的 Term(任期号)。
-
将自己的角色转变为候选者(Candidate)。
-
向其他节点发起投票,开始新一轮的 Leader 选举。
-
-
-
Raft 心跳超时 (Heartbeat Timeout)
:Heartbeat Timeout是Raft协议中的一个重要时间参数,它用于维持Leader节点的权威,并确保Raft Group的稳定运行。Leader的心跳:Leader节点会定期向所有Follower节点发送心跳包。这个心跳包不仅是向Follower证明Leader仍然活跃,也是传递空的日志条目,以维持同步。Follower的计时器:每个Follower节点都有一个心跳计时器。只要Follower能够持续收到来自Leader的心跳,它就会重置这个计时器。- 维持
Leader地位:Leader必须在心跳超时(heartbeat timeout)时间内向所有Follower发送心跳,否则Follower就会认为Leader已经失效。 Heartbeat Timeout与Election Timeout的关系Heartbeat Timeout的值通常比选举超时(Election Timeout)小得多。Heartbeat Timeout的目的在于维持Leader。Election Timeout的目的在于触发选举。
Raft Leader 选举过程

图示中的 Region1 有三个副本,分别位于 TiKV node 1、node 2 和 node 3。在集群创建时,所有节点初始都是 Follower`。
-
Follower转变为Candidate- 在
Term 1期间,TiKV node 2的 Election Timeout 第一个到达, 即长时间没有收到Leader的心跳信号,它认为Leader已经失效。 node 2会将自己的Term增加到2,并将自己的角色从Follower变为Candidate,从而发起新的选举。
- 在
-
Candidate请求投票node 2(现在的Candidate)会向其他所有节点(node 1和node 3)发送投票请求。node 1和node 3收到term比自己的term大的请求后,如果它们还没有投票给其他Candidate,它们会投票给node 2。node 1和node 3都更新了自己的Term为2。
-
选举成功
- 当
node 2收到来自大多数节点的投票(超过一半的节点),它就会赢得选举。 - 此时,
node 2会将自己的角色从Candidate转变为Leader,并开始向其他节点发送心跳。
- 当
-
Follower接受新Leadernode 1和node 3收到来自node 2的心跳后,它们会接受node 2成为新的Leader,并将自己的角色保持为Follower。
这个过程确保了在旧 Leader 宕机后,Raft Group 能够快速、安全地选举出新的 Leader,从而保证服务的持续可用。
Raft Leader 选举过程:故障转移

该图表展示了 Raft Leader 选举在主节点宕机时的具体过程,这是 Raft 协议确保高可用性的核心机制。
-
初始状态
- 在
Term 2中,TiKV node 2是Region1的Leader,而node 1和node 3都是Follower。 - 此时,
TiKV node 2发生宕机。
- 在
-
Follower触发选举- 在
node 2宕机后,node 1和node 3(作为Follower)将无法在选举超时(Heartbeat Timeout)时间内收到来自Leader的心跳。 - 它们会认为
Leader已经失效,并准备发起新的选举。
- 在
-
选举过程
- 在图中,
TiKV node 3首先发起选举。它将自己的Term增加到3,并将角色转变为候选者(Candidate)。 node 3会向其他节点(node 1)发送投票请求。node 1收到请求后,由于它在Term 3中还没有投票,它会投票给node 3。node 3获得了来自大多数节点(node 1和它自己)的投票,因此赢得了选举。
- 在图中,
-
选举成功
node 3成功成为Region1在Term 3中的新Leader。node 3开始向node 1发送心跳,通知它自己是新Leader。node 1接受node 3为新Leader,并将自己的Term更新为3,角色保持为Follower。
这个过程确保了即使 Leader 节点失效,Raft Group 也能在很短的时间内选出新的 Leader,从而保证服务的持续可用。
TiKV 的随机选举超时(Randomized Election Timeout)
在 TiKV 中,Raft 协议的选举超时(election timeout)是一个关键的配置参数,它通过随机化来确保集群的稳定性和高效性。
1. 核心作用
Randomized Election Timeout 的主要目的是避免选举冲突。如果没有随机性,当 Leader 宕机后,所有 Follower 可能会在几乎同一时间都超时,并同时转变为候选者(Candidate),发起投票。这会导致平票(split vote),使得没有节点能够赢得多数票,从而延长选举时间,甚至需要进行多轮选举。
通过随机化,TiKV 确保了每个 Follower 的选举超时时间都是不同的。这使得一个 Follower 更有可能比其他 Follower 先超时,从而赢得选举,并在其他节点进入选举状态前,成为 Leader 并发送心跳。
2. 配置参数
TiKV 配置文件中与随机选举超时相关的参数包括:
raft-base-tick-interval:Raft状态机的基础时钟间隔。默认值为 1 秒。raft-election-timeout-ticks:选举超时时间的基本单位,以tick为单位。默认值为 10。raft-min-election-timeout-ticks:最小的选举超时tick数。raft-max-election-timeout-ticks:最大的选举超时tick数。
最终的随机选举超时时间会在 [raft-min-election-timeout-ticks, raft-max-election-timeout-ticks) 这个范围内随机选择。
3. 示例
在 TiKV 默认配置下:
raft-base-tick-interval= 1sraft-election-timeout-ticks= 10raft-min-election-timeout-ticks= 0(表示使用raft-election-timeout-ticks的值,即 10)raft-max-election-timeout-ticks= 0(表示使用2 * raft-election-timeout-ticks的值,即 20)
因此,默认的随机选举超时时间会在 [10s, 20s) 这个范围内随机选择一个值。
简而言之,TiKV 的随机选举超时机制是 Raft 协议中一个重要的优化手段,它通过引入随机性来确保选举的稳定性和快速性。
Raft Leader 选举与心跳时间参数
-
选举超时(
Election timeout)- 参数:
raft-election-timeout-ticks - 计算:选举超时时间 =
raft-election-timeout-ticks×raft-base-tick-interval - 作用:这个参数定义了
Follower在没有收到Leader消息时等待的时间。如果超时,Follower就会转变为候选者(Candidate),发起新的Leader选举。
- 参数:
-
心跳时间间隔(
Heartbeat time interval)- 参数:
raft-heartbeat-ticks - 计算:心跳时间间隔 =
raft-heartbeat-ticks×raft-base-tick-interval - 作用:这个参数定义了
Leader节点向Follower节点发送心跳包的频率。心跳包用于维持Leader的权威,并防止Follower的选举计时器超时。
- 参数:
-
核心作用总结
raft-base-tick-interval是一个基础时钟间隔,是所有Raft时间参数的最小单位。raft-heartbeat-ticks的值通常远小于raft-election-timeout-ticks。- 这种设计确保了在正常运行状态下,
Leader的心跳会频繁地到达Follower,从而阻止Follower超时并引发不必要的选举。只有当Leader真正失效时,Follower的选举计时器才会超时,并快速触发新的选举,保证了服务的高可用性。

浙公网安备 33010602011771号