PCTA/PCTP 学习笔记-TiDB V6 数据库核心原理与架构(已完结)
本学习笔记由个人为备考 PCTA/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 实现分布式事务的关键,它为每个事务分配一个全局单调递增的时间戳,以确保事务在全集群范围内的顺序和一致性。
TSO 时间戳由两部分组成: - 物理时间戳:自 1970 年 1 月 1 日以来的 UNIX 时间戳,单位为毫秒。 - 逻辑时间戳:递增计数器,用于需要在一毫秒内使用多个时间戳的情况,或某些事件可能触发时钟进程逆转的情况。在这些情况下,物理时间戳会保持不变,而逻辑时间戳保持递增。该机制可以确保 TSO 时间戳的完整性,确保时间戳始终递增而不会倒退。 -
收集集群信息进行调度:PD 会持续从 TiKV 和 TiFlash 存储节点接收心跳信息,以收集节点的负载、存储空间和网络流量等实时状态。PD 利用这些信息来做出智能的调度决策。
-
提供lable,支持高可用。
-
提供 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的选举计时器才会超时,并快速触发新的选举,保证了服务的高可用性。
TiKV- 读写与 Coprocessor
数据的写入

TiDB 的数据写入是一个涉及多个组件的分布式过程,核心组件包括:
- 客户端/用户 (Commit):发起事务提交操作。
- TiDB Server:接收客户端请求,处理 SQL 逻辑,并协调分布式事务。
- PD (Placement Driver):提供集群元信息、全局时间戳服务(TSO),以及调度管理。
- TiKV Node (1, 2, 3):实际的数据存储节点,每个节点运行 Raft 协议来保证数据一致性和高可用性。
每个 TiKV 节点内部主要包含以下用于数据写入的组件:
raftstore pool:处理 Raft 协议的核心线程池,负责 Raft 日志的复制、选举、心跳等操作。apply pool:应用 Raft 日志的线程池。它将已达成多数派共识的 Raft 日志(数据变更)实际写入到底层的存储引擎。rocksdb raft:用于存储 Raft 日志的 RocksDB 实例。rocksdb kv:用于存储实际业务数据(键值对)的 RocksDB 实例。
整个写入流程可以概括为事务协调和Raft 复制两个阶段:
1. 事务协调与数据分发
- 客户端发起
Commit请求到 TiDB Server。 - TiDB Server 执行分布式事务的两阶段提交(
2PC)过程,并从 PD 获取提交时间戳(Commit TS)。 - TiDB Server 将数据变更请求(Key-Value 写入)发送给对应数据的 TiKV Leader 节点(如
TiKV node 2)。
2. Raft 协议数据复制
- Leader 接收:Leader 节点(例如
TiKV node 2)的raftstore pool接收到写入请求。 - 写入 Raft 日志:
raftstore pool将数据变更封装为一条 Raft 日志,并将其写入到本地的rocksdb raft存储中。 - 日志复制:Leader 通过 Raft 协议(图中绿色的虚线)将该日志并行发送给所有 Follower 节点(
TiKV node 1和TiKV node 3)的raftstore pool。 - 多数派确认:当 Leader 收到多数派 Follower 节点的确认(日志已写入其本地的
rocksdb raft)后,Raft 协议认为该日志已提交(Committed)。
3. 数据应用与持久化
- 通知应用:Leader 和 Follower 节点的
raftstore pool一旦确认日志已提交,就会将该日志转发给apply pool。 - 实际写入 KV:
apply pool负责执行日志中的数据变更操作,将其最终写入到rocksdb kv存储中(图中红色的虚线)。 - 写入完成:只有数据成功写入
rocksdb kv后,整个数据变更才算真正完成持久化,并对用户可见。
数据的读取
ReadIndex Read (这里暂不考虑MVCC)

TiKV 采用 ReadIndex Read 机制来保证读取的线性一致性 (Linearizability),即读取操作必须看到所有在它之前完成的写入。
- 获取快照版本和 TiKV Leader 节点:客户端发起
read请求,TiDB Server 向 PD 请求一个 快照时间戳 (TS),并查询数据在哪个TiKV Leader 节点。 - Leader 接收:请求被发送到对应的 TiKV Leader 节点的
raftstore pool。为确保 Leader 自身状态不是过期的(即避免“脑裂”读取),Leader 会向多数派 Follower 发送一个 心跳信息,以确认自己仍是 Leader。 - 多数派确认:Leader 收到多数派的确认后,确认自己仍是 Leader。
- 发起 ReadIndex:Leader 获取当前最新的 CommitIndex(例如 CommitIndex= 1_97)作为 ReadIndex,这个索引表示 Leader 已经将 Raft 日志 提交到 rocksdb kv 的最新进度。。
- 等待 ApplyIndex:Leader 必须等待自身的
ApplyIndex不小于ReadIndex。- 例如:如果
ReadIndex = 1_97,Leader 必须确保其ApplyIndex达到1_97,表示其本地rocksdb kv已经写入了所有已提交到1_97的数据。
- 例如:如果
- 读取快照:一旦条件满足,请求转发给
apply pool,根据最初获取的快照 TS,从rocksdb kv中读取对应版本的数据。
Lease Read (基于 Leader 租约)
Lease Read 机制在 Leader 租约期内(Leader 认为自己不会被选掉的时间段)可以跳过 ReadIndex 步骤,直接读取数据,从而降低延迟。
- Leader 租约:Leader 通过定期向 Follower 发送心跳来维护其 Leader 身份,这段 Leader 身份的保证期就是租约 (Lease)。
- 例如,如果在选举超时 (Election Timeout) 之前没有发生 Leader 切换,Leader 就可以认为在租约期内自己是稳定的 Leader。
- 读取过程:在租约期内,Leader 可以直接从本地的
rocksdb kv中读取数据,无需与多数派 Follower 进行确认,性能更高。
Follower Read (Follower 读)

Follower Read 允许 Follower 节点提供读取服务,以分摊 Leader 的读取压力。为了保证一致性,Follower 在读取前必须确认自己的数据已经同步到最新。
- Leader 确认 CommitIndex:Follower (例如
TiKV node 3) 向 Leader (例如TiKV node 2) 询问当前的CommitIndex。 - CommitIndex 同步:Leader 返回当前的
CommitIndex(例如1_97)。 - 等待 Apply:Follower 必须等待自身的
ApplyIndex(当前应用到 KV 存储的进度)追上或超过这个返回的CommitIndex。- 如图所示,Follower 的
ApplyIndex需要从1_90追赶到1_97。
- 如图所示,Follower 的
- 读取数据:一旦
ApplyIndex满足要求,Follower 即可从本地的rocksdb kv中读取数据并返回给 TiDB Server。
特殊情况:如果 Follower 写入速度比Leader快,会出现 Follower 的 apply 速度超过 Leader 的情况。
Coprocessor 机制:计算下推
Coprocessor 是一种将计算逻辑从 TiDB Server 推向 TiKV 存储层以实现分布式并行计算的机制。
- 查询发起:用户在 TiDB 上发起查询,例如
select count(*) from T;。 - 计算下推:TiDB Server 不拉取全部数据进行计算,而是将 聚合/过滤 等计算逻辑推送到各个 TiKV 节点上的 Coprocessor 实例。
- 分布式计算:
- 每个 TiKV 节点(Node 1, 2, 3)上的 Coprocessor 负责处理存储在其本地的数据分片。
- Coprocessor 在本地完成计算(例如,计算每个分片的
count(*)),显著减少了网络传输的数据量。
- 结果合并:TiDB Server 接收各个 TiKV 节点返回的局部计算结果,并在 TiDB 层进行最终的聚合/合并,得出最终结果。
Placement Driver
功能概述
是整个集群的“大脑”,负责管理集群的元数据和全局协调。一个PD (Placement Driver) Cluster由至少 3 个PD 节点构成,拥有高可用的能力。建议部署奇数个PD 节点。
-
整个集群的元数据存储:PD 存储了集群中所有 Region(数据块)的元数据信息,包括每个 Region 的位置和副本分布情况。这使得 PD 能够全面掌握集群的实时状态。
-
分配全局 ID 和事务 ID :PD 充当着全局 ID 和事务 ID 的分配中心。它为每个事务分配一个唯一的事务 ID,确保了事务的 ACID 特性,特别是在处理并发操作时,能够保证数据的正确性和一致性。
-
生成全局时间戳 TSO:TSO 是 TiDB 实现分布式事务的关键,它为每个事务分配一个全局单调递增的时间戳,以确保事务在全集群范围内的顺序和一致性。
TSO 时间戳由两部分组成: - 物理时间戳:自 1970 年 1 月 1 日以来的 UNIX 时间戳,单位为毫秒。 - 逻辑时间戳:递增计数器,用于需要在一毫秒内使用多个时间戳的情况,或某些事件可能触发时钟进程逆转的情况。在这些情况下,物理时间戳会保持不变,而逻辑时间戳保持递增。该机制可以确保 TSO 时间戳的完整性,确保时间戳始终递增而不会倒退。 -
收集集群信息进行调度:PD 会持续从 TiKV 和 TiFlash 存储节点接收心跳信息,以收集节点的负载、存储空间和网络流量等实时状态。PD 利用这些信息来做出智能的调度决策。
-
提供lable,支持高可用
-
提供 TiDB Dashboard 服务:PD 集群集成了 TiDB Dashboard,这是一个可视化的监控和管理工具,可以帮助运维人员直观地查看集群的各项指标和运行状态。
路由功能
PD 维护着最新的集群拓扑和 Region 元信息,将最新的 Leader 节点位置返回给 TiDB Client。
- Executor 请求:当 Executor 需要访问特定 Key Range 的数据时,它会向 TiKV Client 发起 Locate Region 请求。
- 查询 Region Cache:TiKV Client 首先检查本地的 Region Cache。
- 如果缓存命中且信息有效,TiDB Server 会直接将请求路由到对应的 TiKV 节点。
- PD 协助:如果 Region Cache 未命中或缓存的 Leader/Region 信息过期,TiDB Client 会向 PD 发送 Locate Region 请求。
- PD 将最新的 Leader 节点位置返回给 TiDB Client。
- 更新缓存与请求下发:TiKV Client 根据 PD 返回的信息更新 Region Cache,然后将读写请求发送给正确的 TiKV Leader 或 Follower 节点。
TSO 分配
PD负责为事务提供大量 TSO,为每个事务分配一个全局单调递增的 TSO,以确保事务在全集群范围内的顺序和一致性。
-
TSO
- 数据结构:TSO 是一个 64 位整数 (int64),精确到毫秒,1毫秒可以提供262144个TSO数。
- 组成: TSO = physical time + logical time
- 物理时间 (physical time):高位部分,表示实际的时间,通常是毫秒级的时间戳。
- 逻辑时间 (logical time):低位部分,表示在同一物理时间片内,发生的事务顺序。
-
TSO 分配过程

整个 TSO 分配过程主要发生在 TiDB Server 内部的 TSO 请求者和 PD Client 之间,并与 PD Leader 进行通信。
- 请求 TSO:TiDB Server 内部的 TSO 请求者发起请求,要求获取一个全局唯一的时间戳。
- 返回 Future:PD Client 不会立即阻塞等待 TSO,而是立即返回一个
tsFuture对象。这个 Future 代表了未来将要返回的 TSO。 - 异步请求:PD Client 同时向 PD Leader 发起异步请求 TSO 的操作,TSO 请求者继续执行自己的解析编译步骤。
- 等待 Future:TSO 请求者在需要使用 TSO 时(例如,事务开始或提交时),会调用
tsFuture.wait方法,阻塞等待 TSO 结果。 - PD 分配 TSO:PD Leader 负责分配 TSO,并将分配好的 TSO 返回给 PD Client。
- 获得 TSO:PD Client 收到 TSO 后,完成
tsFuture,TSO 请求者通过tsFuture.wait成功获得 TSO。
-
性能优化:时间窗口
为了提高效率、缓解IO压力等性能问题,PD 不会每次只分配一个 TSO,而是分配一个时间窗口,批量生成TSO值,供 TiDB Server 使用。
- TiDB 请求:大量 TiDB Server (Client) 同时向 PD Leader 请求 TSO。
- 时间窗口分配:PD Leader 响应请求时,会分配一个 TSO 时间窗口(Time Window),在窗口范围内批量在PD Leader缓存中生成TSO值队列。
- 窗口起始:窗口从当前分配的 TSO 开始(例如
700)。 - 窗口大小:PD Leader 会预留一个范围(例如预留 3 秒)。
- 窗口起始:窗口从当前分配的 TSO 开始(例如
- TiDB 内部使用:在 PD 返回时间窗口后,TiDB Server 可以在这个窗口范围内(例如从
700到703)为并发事务快速分配 TSO(排队获取),而无需再次请求 PD。 - 提高性能:通过一次请求获取一批可用的 TSO,显著减少了 TiDB Server 与 PD Leader 之间的 磁盘IO 和网络往返次数。
- 高可用优化:Raft协议
- PD Leader挂掉之后,会启动Raft协议重新选主,新Leader从最大TSO重新分配,保证增长性。
- 旧leader 缓存内的TSO全部丢失,无法保证TSO的连续性。
信息收集
为了做出正确的调度决策(例如负载均衡、Leader 转移、数据迁移),PD 必须周期性收集来自 TiKV 节点的实时状态信息。
PD 通过接收来自 TiKV 节点的心跳 (Heartbeat) 来收集集群的两种关键信息:Store 状态和 Region 状态。
-
Store Heartbeat (存储心跳)
- 发起方:每个 TiKV 节点(Store)定期向 PD 发送心跳。
- 目的:报告整个 TiKV 节点的状态,包括磁盘使用情况、CPU/内存负载、网络延迟等,用于 PD 的负载均衡和健康检查。
-
Region Heartbeat (分片心跳)
- 发起方:每个 Region 的 Leader 节点定期向 PD 发送心跳。
- 目的:报告该 Region 的元信息和状态,包括:
- Region 的键范围。
- Leader 的位置:报告当前 Leader 所在的 TiKV 节点。
- Follower 的状态:报告 Region 副本在其他 TiKV 节点上的同步情况。
调度
PD 收集以上心跳信息后,建立集群的全局视图,并根据预设的策略(如 Leader 数量、空间使用)生成调度指令,以维护集群的健康、负载均衡和高可用性。
PD 生成调度指令的主要目标包括:
- Balance (均衡):
- Leader 均衡:将 Region Leader 均匀分布到所有 TiKV 节点上,以防止单个节点成为读写热点。
- Region 均衡:将 Region 副本均匀分布到所有 TiKV 节点上,以均衡存储空间和数据访问压力。
- Hot Region (热点处理):识别并分散读写压力过大的热点 Region。
- 集群拓扑:根据集群的物理拓扑结构(如机架、数据中心),确保 Region 副本分散在不同的容灾域,提高系统的容错能力。
- 缩容:在移除 TiKV 节点时,将节点上的所有 Region Leader 和副本安全迁移走。
- 故障恢复:在 TiKV 节点发生故障时,快速将受影响的 Leader 转移到健康的节点,并对缺失的副本进行修复。
- Region merge (Region 合并):将相邻的、数据量较小的 Region 合并成一个大的 Region,以减少 Region 总数,降低元信息管理的开销。
Label 与高可用

TiDB/TiKV 集群通过 Label (标签) 机制来描述其物理拓扑结构。PD (Placement Driver) 利用这些标签信息来智能地分布 Region 副本,确保数据副本分散在不同的故障域,从而实现高可用性 (High Availability)。
拓扑结构与 Label
以下是基于 TiKV 节点物理位置的 Label 拓扑层级划分:
| 拓扑层级 | 标签示例 | 包含的 TiKV 节点 | 备注 |
|---|---|---|---|
| 数据中心 (DC) | DC 1, DC 2, DC 3 |
TiKV-1 至 TiKV-12 | 最高级别的故障域 |
| 机架 (Rack) | Rack 1, Rack 2, Rack 3, Rack 4, Rack 5, Rack 6 |
TiKV-1, TiKV-2 属于 Rack 1;TiKV-7, TiKV-8 属于 Rack 4 等 |
中级别的故障域 |
| 主机 (Host) | 通常位于 Rack 下 | 通常一个主机运行多个 TiKV 实例,是比 Rack 更小的故障域。 | |
| TiKV 节点 | TiKV-1, TiKV-2, ..., TiKV-12 |
最小级别的存储单元 | 最小单元,承载 Region 副本 |
每个 TiKV 节点在启动时,都会配置自己的 server.labels,用于描述其所在的物理环境。
- TiKV-1 示例配置:
server.labels: { "zone": "1", "rack": "1", "host": "1" } - 表示 TiKV-1 位于 DC 1、Rack 1、服务器 1。
PD 配置与调度策略
PD 在集群级别通过配置来定义如何使用这些标签进行调度。
location-labels:定义了 PD 关注的拓扑层级。location-labels = ["zone", "rack", "host"]- 这告诉 PD,副本分布应该在
zone、rack、host三个层级上进行考虑。
- 这告诉 PD,副本分布应该在
isolation-level:定义了 PD 在副本放置时必须保证的最低隔离级别。isolation-level = "zone"- 这意味着 PD 必须确保一个 Region 的所有副本至少分布在不同的 Zone 上,从而保障了高可用性。
Region 副本的高可用分布
PD 的调度策略确保一个 Region 的多个副本不会集中在同一个故障域内。
这种跨机架和跨数据中心的分布机制,保证了即使一个机架或一个数据中心发生故障,数据仍然完整且可用。
TiDB 数据库 HTAP 概述
HTAP 技术
HTAP (Hybrid Transactional/Analytical Processing) 技术旨在将 OLTP(在线事务处理)和 OLAP(在线分析处理)的工作负载整合到同一个数据库系统中。
HTAP 数据库需要同时满足这两种不同工作负载的需求。
| 特性维度 | OLTP (在线事务处理) | OLAP (在线分析处理) |
|---|---|---|
| 数据更新 | 支持实时更新的行存 | 批量更新的列存 |
| 并发性 | 高并发,对数据一致性要求高 | 并发数低 |
| 数据量 | 每次操作少量数据 | 每次操作大量数据 |
OLTP 与 OLAP 传统流程

在传统的企业数据架构中,事务处理 (OLTP) 和分析处理 (OLAP) 通常是分离的系统。OLTP 系统负责实时业务操作,而 OLAP 系统(如数据仓库/数据湖)负责进行大规模数据分析。
数据流
- OLTP -> OLAP:数据通过 ETL 流程从 OLTP 数据库进入 Data Warehouse / Lake (数据仓库/湖)。
- OLAP -> 其他系统:数据仓库/湖中的数据可能再次通过 ETL 流程传输到其他下游系统(如报表、BI 工具)。
传统架构问题
这种分离的架构通过 ETL (Extract, Transform, Load) 过程连接,但也带来了两个主要问题:
- ETL 带来的分析延迟:
- 描述:数据从 OLTP 数据库传输到 OLAP 系统的过程中,需要经过 ETL 批处理流程。
- 影响:这个 ETL 过程引入了数据延迟,导致分析不能实时进行。
- 多副本问题:
- 描述:OLTP 与 OLAP 的业务分离,意味着需要在不同的数据库中存储相同业务数据的多个副本。
- 影响:这不仅造成了存储资源的浪费,还增加了数据维护的复杂性。
HTAP 的要求
HTAP (Hybrid Transactional/Analytical Processing) 混合事务/分析处理系统旨在支持 OLTP(高并发事务)和 OLAP(大规模分析)两种截然不同的工作负载。
HTAP 系统需要满足以下三个维度的核心要求:
- 可扩展性 (Scalability):
- 分布式事务:系统必须支持跨多个节点的事务操作,并保证一致性。
- 分布式存储:系统需要具备分布式存储能力,以便横向扩展存储和计算资源。
- 同时支持 OLTP 与 OLAP:
- 同时支持行存与列存:需要存储结构能够同时高效支持面向行(适用于 OLTP 事务)和面向列(适用于 OLAP 分析)的存储方式。
- OLTP 与 OLAP 业务隔离:必须确保 OLAP 的大规模分析查询不会对 OLTP 的高并发事务造成性能影响或阻塞。
- 实时性 (Real-time):
- 行存与列存数据实时同步:分析需要基于最新的事务数据,因此行存和列存之间的数据必须保持实时同步。
TiDB 的 HTAP 架构
TiDB 采用 HTAP (Hybrid Transactional/Analytical Processing) 架构,旨在统一支持 OLTP(事务)和 OLAP(分析)工作负载。

TiDB 的 HTAP 架构主要由三个逻辑层组成:
-
PD Cluster (调度和 TSO)
- 组件:由多个 PD 节点组成。
- 功能:负责集群的元信息管理、调度(Placement Driver),以及提供全局唯一的时间戳服务 (TSO)。
-
TiDB Server (SQL 层)
- 组件:由多个 TiDB Server 实例组成。
- 功能:接收客户端通过 MySQL 协议发送的应用程序请求。负责 SQL 解析、查询优化、执行计划生成,并协调分布式事务。智能选择使用TiKV还是TiFlash。
-
Storage Cluster (存储层)
- 组件:包括 TiKV 和 TiFlash 实例。
- TiKV:
- 存储方式:提供行存,适用于 OLTP 事务处理。
- 功能:基于 Raft 协议保证强一致性和高可用性。
- TiFlash:
- 存储方式:提供列存,适用于 OLAP 实时分析。
- 功能:以第三角色加入Tikv的multi-raft组,异步复制TiKV的数据,是 TiDB 存储引擎的列式副本,用于将分析计算下推到存储层。
TiDB 的 HTAP 特性
- 行列混合:
- 列存 (TiFlash):TiDB 通过 TiFlash 组件提供列式存储。
- TiFlash 支持基于主键的实时更新。
- TiFlash 作为列存副本:TiFlash 是 TiKV 行存数据的一个列存副本。
- 列存 (TiFlash):TiDB 通过 TiFlash 组件提供列式存储。
- OLTP 与 OLAP 业务隔离:
- 目的:确保大规模的分析查询(OLAP)不会干扰到高并发的事务处理(OLTP)性能。
- 智能选择 (CBO):
- CBO:查询优化器 (Cost-Based Optimizer) 可以自动或通过人工干预来选择最佳的执行路径。
- 作用:优化器智能决定查询应该使用 TiKV 的行存还是 TiFlash 的列存进行处理。
- MPP 架构:
- MPP (Massively Parallel Processing):TiFlash 采用了 MPP 架构。
- 作用:加速TiFlash上的聚合连接操作。
MPP (大规模并行处理)
MPP (Massively Parallel Processing) 架构是 TiFlash 组件用于加速大规模数据分析查询(OLAP)的核心技术。TiDB Server 在此架构中充当查询的协调者 (coordinator)。
核心特性
- 处理大查询:MPP 专门用于处理大量数据的 join 聚合查询。
- 内存计算:所有 MPP 计算都在 TiFlash 节点的内存中完成,以追求极致的分析性能。
- 连接限制:目前 MPP 只支持等值连接。
- 启用验证:可以使用
Enforce_mpp选项来帮助验证查询是否可以使用 MPP 模式。
架构流程

- 查询路由:
- OLTP 业务:TiDB Server 依然将 OLTP 请求路由给 TiKV 节点进行处理。
- MPP 任务:对于分析型查询,TiDB Server (作为协调者) 将查询任务分解并下发给多个 TiFlash 节点。
- TiFlash 间的通信:TiFlash 节点之间可以直接通过网络进行数据交换和计算协作,这种通信机制能够将相关的数据移动交换到一个 TiFlash 节点,使得表连接、聚合等计算操作只在同一个TiFlash 节点上进行。
Join 查询示例
以下面的复杂分析查询为例:
select count(*) from order, product
where order.pid = product.pid
and sub_str(order.dic, 3) = '7c0'
and product.pct_date > '2021-09-30'
group by order.state;
1. 过滤 (Predicate Pushdown)
在计算下推到 TiFlash 节点后,首先执行本地的过滤操作,显著减少数据量:
- TiFlash 节点 1:对本地的
order和product分片,应用product.pct_date > '2021-09-30'和sub_str(order.dic, 3) = '7c0'等过滤条件。 - 作用:每个 TiFlash 节点只处理并返回满足条件的本地数据。
2. 数据交换与 Join
MPP 架构通过 TiFlash 节点间的数据交换,实现高效的分布式 JOIN 操作:
- 数据交换:TiFlash 节点之间直接通过网络进行数据传输和计算协作。
- 哈希分区:为了实现分布式 JOIN,数据通常根据 JOIN Key (例如
product.pid) 进行哈希分区,将相关联的数据行移动到同一个 TiFlash 节点进行连接。- 示例:根据
pid哈希值0, 1, 2的数据被分配到第一个 TiFlash 节点,哈希值3, 4, 5, 6分配到第二个 TiFlash 节点,以此类推。
- 示例:根据
- 分布式 JOIN:每个 TiFlash 节点在其本地独立完成 JOIN 操作。
3. 聚合与结果
完成 JOIN 后,每个 TiFlash 节点独立地在其本地数据分片上执行部分聚合,注意 group by 也需要进行数据交换操作。
在本地完成聚合计算 (group by order.state 和 count(*))。最终结果返回给 TiDB Server 进行汇总。
TiFlash
Tiflash 架构

TiFlash 是 TiDB HTAP 架构中的核心组件,它作为 TiKV 数据的列式存储副本。TiFlash 基于 Raft Learner 机制实时同步 TiKV 的数据,用于加速 OLAP(在线分析处理)查询。
TiFlash的Region和Tikv上的Region一一对应,同时兼容TiSpark/TiDB Server.
TiFlash无法写入数据,只能从Tikv复制。
TiFlash 通过 Raft Learner 角色实时从 TiKV 复制数据,保证了分析数据的实时性。
- Raft Group:TiKV 的数据分片(Region)由 Raft Group 维护。
- Raft Learner:TiFlash 节点作为该 Raft Group 的一个特殊成员——Raft Learner。
- 数据复制:Raft Learner 接收来自 Raft Leader (位于某个 TiKV 节点) 的日志。
- TiFlash 节点将 Leader 复制过来的数据,从行式存储实时转换为列式存储,并将其存储在 TiFlash 内部。
TiFlash 主要功能
- 异步复制:TiFlash 采用异步方式从 TiKV Leader 接收 Raft Log,实时复制数据。
- 一致性读取:TiFlash 支持一致性读取,确保分析结果基于最新的事务数据。
- 引擎智能选择:TiDB 的查询优化器 (CBO) 能够智能地决定使用 TiKV 行存引擎还是 TiFlash 列存引擎来执行查询。
- 计算加速:通过列式存储和 MPP (大规模并行处理) 架构,TiFlash 极大地加速了分析计算的性能。
TiFlash 主要功能:异步复制
TiFlash 作为 TiDB HTAP 架构中的列式存储副本,必须确保其数据与 TiKV 的行存数据保持实时同步。TiFlash 通过 Raft Learner 机制实现数据的异步复制。
数据同步机制:Raft Learner
- 数据源:TiKV Leader (例如
TiKV*节点) 负责 Region 的数据写入和 Raft 日志的复制。 - Learner 角色:TiFlash 节点作为 Raft Group 的一个特殊成员,身份是 Learner。
- 异步复制:
- Leader 将 Raft 日志发送给 Learner。
- 复制过程是异步的,不影响 Raft 多数派的提交性能(即 TiKV 的写入延迟)。
- Learner 特性:Learner 节点具有以下关键特性:
- 不参与 Raft 投票:不对 Raft 提交达成共识产生影响。
- 不参与 Raft 选举:不具备被选为 Leader 的资格。
- 基于主键快速更新:TiFlash 能够基于主键,高效地将接收到的行存日志转换为列式存储。
TiFlash 主要功能:一致性读取
TiFlash 作为 TiKV 数据的列存副本,通过 Raft Learner 机制接收数据。为了确保分析结果的正确性,TiFlash 必须支持一致性读取,即读取到的数据必须是已提交的最新版本。
一致性读取流程 (TiFlash Read)
当客户端发起一个读取请求时,TiFlash 会执行以下步骤来保证一致性:
- 确认日志进度:TiFlash 节点在读取数据前,会向对应的 Raft Leader (TiKV) 发送
Confirm raft log idx消息。 - Leader 响应:Leader 返回其当前最新的 Raft Log 进度。
- 等待应用 (Apply):TiFlash 必须等待其本地的 Raft Log 全部被应用到列存中。
- 示例:如果 TiFlash 的 Raft Log 已经同步到了
idx=125,它将等待应用该索引及其之前的所有日志。
- 示例:如果 TiFlash 的 Raft Log 已经同步到了
- 读取数据:一旦数据应用完成,TiFlash 即可根据读取请求的时间戳(快照)从列存中安全地读取数据。
示例
| 时间点 | 操作 | TiKV Leader 状态 (Raft Log) | TiFlash 状态 (Raft Log 进度) |
|---|---|---|---|
| T0 | 写入 key=1, value=100 |
对应 idx=101 |
异步复制中 |
| T1 (Read) | 读取 key=1 |
维持 value=100 |
TiFlash 必须确认已应用 T1 之前的日志,然后读取 value=100 |
| T2 | 写入 key=1, value=200 |
对应 idx=122 |
异步复制中 |
| T3 (Read) | 读取 key=1 |
维持 value=200 |
向 Raft Leader 确认 Raft Log 最新值,接收到 idx=122,同步到idx=122之后读取 value=200 |
TiFlash 主要功能:引擎智能选择
引擎智能选择是 TiDB 查询优化器 (CBO) 的核心功能之一。它允许系统根据查询的类型和成本,自动或通过人工干预决定是使用 TiKV 行存引擎还是 TiFlash 列存引擎来执行查询。
智能选择流程
对于一个复杂的查询,TiDB Server 会将其拆解,并为每个部分选择最优的执行路径。
1. 查询分析
以查询示例 SELECT AVG(sales.price) FROM product, sales WHERE product.pid = sales.pid AND product.batch_id = 'B1328'; 为例:
- 目标:计算平均销售价格(聚合操作
AVG),并涉及product和sales两个表的连接(JOIN)。 - 过滤条件:存在基于
product.batch_id的高选择性过滤条件。
2. 优化器决策
优化器根据查询的特性和表的统计信息做出决策:
- OLTP 路径 (TiKV):
- 对于涉及高选择性过滤(利用索引)的操作,优化器选择 IndexScan(索引扫描)。
- 在示例中,
product表的过滤部分被路由给 TiKV 节点,执行 IndexScan: product,利用索引快速定位product.batch_id = 'B1328'的行。
- OLAP 路径 (TiFlash):
- 对于涉及全表扫描、聚合计算(如
AVG)或列式扫描的操作,优化器选择 TableScan(全表扫描)并路由给 TiFlash 节点。 - 在示例中,
sales表的扫描(TableScan: sales (pid, price))被路由给 TiFlash 节点。
- 对于涉及全表扫描、聚合计算(如
3. 执行与合并
TiDB Server 接收来自 TiKV 和 TiFlash 的局部结果,并在 TiDB 层进行最终的 JOIN 和聚合操作。
TiDB 6.0 新特性
SQL 放置规则 (Placement Rules in SQL)
Placement Rules in SQL 是 TiDB 提供的一种高级调度机制,允许用户通过 SQL 语句定义数据副本的放置策略、副本数量以及流量路由,从而解决跨地域部署和业务资源隔离等复杂问题。
TiDB 6.0 之前
在没有 Placement Rules in SQL 时,TiDB 难以在复杂的跨地域部署场景下,实现精细化的资源隔离和流量控制。
| 挑战 | 描述 | 示例 |
|---|---|---|
| 无法本地访问 | 跨地域部署的集群,用户请求无法保证访问到本地的 TiKV 节点,可能导致跨地域访问延迟增加。 | Beijing 的用户可能访问到 NewYork 或 Tokyo 的数据副本。 |
| 无法隔离资源 | 缺乏细粒度的机制将不同业务的数据(Region 副本)隔离到特定的资源组。 | 各种业务/表(T2, T3, T4, T5, T6, T7, T8)的 Region 副本混杂在所有 TiKV 节点上,资源相互影响。 |
| 难以按等级配置 | 难以按照业务等级来定制化配置 Region 的副本数或资源优先级。 | 所有业务采用统一的副本配置,无法为高等级业务提供特殊保障。 |
TiDB 6.0 之后
通过在 SQL 中配置 Placement Rules,PD (Placement Driver) 可以根据用户定义的策略进行调度,实现更智能的数据放置和流量路由。
| 核心能力 | 描述 | 示例 |
|---|---|---|
| 支持本地访问 | 跨地域部署的集群,可以配置规则将 Leader 或 Follower 副本路由到靠近用户的地域,支持本地读写访问,大幅降低延迟。 | Beijing 的用户请求优先路由到 Beijing 区域内的 T2*、T6、T3 副本。 |
| 根据业务隔离资源 | 可以将不同业务(例如 T2 业务)的数据副本隔离到特定的 TiKV 节点组上,实现资源独占和业务隔离。 | 业务 T2 的副本集中在 TiKV-1, 2, 3,业务 T7 的副本集中在 TiKV-7, 8,9,10,互相不影响。 |
| 按业务等级配置 | 可以按照业务等级配置不同的资源和副本数,满足不同业务对高可用性和性能的要求。 | 为 T7 业务配置特定的副本数量。 |
使用步骤
使用 Placement Rules in SQL 通常分为三个主要步骤:设计拓扑、创建策略、设定对象策略。
1. 设计业务拓扑与设置标签
- 目标:设计集群的物理拓扑结构,并为不同的 TiKV 实例设置对应的 Label (标签)。
- 示例配置:
server.labels: { "zone": "Beijing", "rack": "Rack-1", "host": "TiKV-1" }- 此标签描述了 TiKV-1 实例的地理位置和主机信息。
2. 创建 Placement Policy (放置策略)
- 目标:通过 SQL 语句创建具体的放置策略,定义副本的数量和放置规则。
- 示例 SQL:
CREATE PLACEMENT POLICY P1 PRIMARY_REGION = "TiKV-5" REGIONS = "Beijing, Tokyo, ShangHai, London" FOLLOWERS = 4;PRIMARY_REGION:定义 Leader 角色 Region 的位置。REGIONS:定义 Follower 角色 Region 的位置。FOLLOWERS:定义副本数量。
3. 设定数据对象的 POLICY
- 目标:将创建好的放置策略 (
POLICY) 应用到特定的数据库对象上。 - 支持对象:策略支持应用于 schema (数据库)、表 (table) 和分区 (partition)。
- 示例 SQL:
CREATE TABLE T5 (id INT) PLACEMENT POLICY=P1;- 将表 T5 的所有 Region 副本放置和调度遵循策略 P1 的规则。
优势总结
Placement Rules in SQL 提供了在数据库层面管理数据副本和流量路由的强大能力。
- 精细化数据放置:用于控制数据的本地访问与跨区域访问,以优化延迟。
- 高可用性与可靠性:通过指定副本数,提高重要业务的可用性和数据可靠性。
- 业务隔离:将业务按照等级、资源需求或者数据生命周期进行隔离。
- 数据整合:实现业务数据的整合,降低运维成本与复杂度。
热点小表缓存
见 Tidb Server -> 7. TiDB Server 的 缓存 -> 热点小表缓存
TiDB 6.0 内存悲观锁优化
概述
-
TiDB 6.0 之前
- 机制:在使用悲观锁的事务中,锁信息在二阶段提交的过程中会被持久化到所有副本节点(Leader 和 Follower)的磁盘中。
- 问题:这种机制导致了不必要的磁盘 I/O 和 Raft 日志复制开销,造成了资源浪费。
-
TiDB 6.0 之后
TiDB 6.0 引入了内存悲观锁优化,改变了锁信息的存储方式,旨在提升悲观事务的性能。
-
核心机制
- 只写 Leader 缓存:优化后,悲观锁的锁信息只写入 Leader 角色 Region 所在节点的内存缓存中,不再持久化到磁盘,也不通过 Raft 协议同步给 Follower。
- 减少开销:由于跳过了磁盘写入和网络复制,显著减少了延迟和资源消耗。
内存悲观锁使用
在 TiDB 集群中可以在线开启内存悲观锁功能,无需重启服务。通过执行 SQL 语句修改 TiKV 的配置来实现。
配置命令:
> set config tikv pessimistic-txn.pipelined='true';
> set config tikv pessimistic-txn.in-memory='true';
应用优势
- 减少事务的延时 (Reduce transaction latency)
- 原因:因为锁信息不需要写入磁盘(WAL)也不需要等待 Raft 共识(复制到多数派节点),加锁操作变成了纯内存操作,速度极快。
- 降低磁盘和网络带宽 (Reduce disk and network bandwidth)
- 原因:避免了锁记录产生的 Raft 日志复制流量和磁盘 I/O 写入。
- 降低 TiKV 的 CPU 消耗 (Reduce TiKV CPU consumption)
- 原因:省去了序列化日志、Raft 流程处理、RocksDB 写入等复杂的 CPU 计算过程。
应用劣势
- 锁丢失问题 (Lock loss issue)
- 解释:这是一个副作用。因为锁只存在于 Leader 的内存中,如果发生 TiKV 宕机或 Leader 迁移(Transfer Leader),锁信息会丢失。适合锁冲突严重、对延迟敏感、且希望能提升吞吐量的场景。牺牲了锁的持久性(Region Leader 变更时锁失效),换取了更高的性能。
- 后果:虽然锁丢了,但在 TiDB 的事务模型中,这通常不会导致数据不一致,但可能会导致正在进行的事务失败并触发重试。
Top SQL
Top SQL 出现之前
在引入 Top SQL 功能之前,数据库运维(DBA)在排查性能抖动时面临着监测数据“断层”的问题。
- 现象可见但原因不明:
- 通过 Grafana 监控面板,可以清晰地看到集群的 CPU 占用率突然发生了显著变化,出现剧烈的飙升。
- 甚至可以定位到具体的节点,发现 个别 TiKV 实例的 Coprocessor CPU 非常高。
- 诊断困难:
- 虽然监控到了 CPU 异常,但很难直接知道是哪条 SQL 导致的。
- 核心难题:慢查询日志(Slow Query Log)只能告诉你哪些 SQL 跑得慢,但跑得慢的 SQL 不一定是消耗 CPU 最多的 SQL。因此,从 CPU 监控图到具体 SQL 列表之间缺乏直接的映射关系。
Top SQL 出现之后
Top SQL 功能填补了上述的断层,提供了一个直观的可视化界面来定位热点 SQL。
- 指定实例:可以筛选指定的 TiDB 及 TiKV 实例进行查看。
- 实时洞察:查看正在执行的 SQL 语句。
- 定位元凶:自动筛选出 CPU 开销最多的 Top 5 类 SQL。(目前只支持监测CPU负载)
- 详细指标:提供每秒请求数、平均延迟等详细执行信息。
TiDB Enterprise Manager (TiEM)
-
面临的任务 (Tasks)
- 企业在管理 TiDB 集群时需要处理大量繁杂的任务,包括:部署集群、升级集群、参数管理、组件管理、备份恢复与高可用管理、集群监控与告警、集群日志收集以及审计与安全。
-
面临的问题 (Problems)
- 数量增长:随着业务发展,集群数量、节点数量、组件数量以及所需的工具数量都在快速增长。
- 复杂度增长:随之而来的是配置参数的复杂性、命令行操作的繁琐以及管理接口的复杂化。
-
解决方案:TiDB Enterprise Manager (TiEM)
- TiEM 旨在通过图形化界面简化上述管理工作,其主要功能包括:
- 一站式管理:支持一键部署集群,并能在一个平台上管理多套集群。
- 自动化运维:支持集群的原地升级。
- 配置简化:提供可视化的参数管理功能。
- 高可用支持:支持克隆集群以及主备集群的切换。
- TiEM 旨在通过图形化界面简化上述管理工作,其主要功能包括:
TiDB Cloud
- 定义:TiDB Cloud 是一个功能齐全的数据库即服务,也称为 DBaaS(Database-as-a-Service)。TiDB Cloud 就是将 TiDB 复杂的分布式架构封装为云端服务,让用户可以直接使用而无需自行管理底层基础设施。
- 架构基础:它是构建在标准 TiDB 架构之上的云托管服务,其内部包含了 PD(Placement Driver)、TiDB Server、TiKV Server 以及 TiSpark 等核心组件,通过云服务层对外提供统一的数据库服务。

浙公网安备 33010602011771号