目标定位:平凯数据库认证PCTA,要求掌握 TiDB架构原理、安装部署及周边工具。利用已搭建的三节点集群与 Sysbench 测试数据,由浅入深逐一验证 TiDB 的基础能力,此文章记录一些本人的学习笔记。
环境概览与测试数据准备
测试集群
已部署一套标准生产高可用集群,三节点均位于 `192.168.151.0/24` 网段,组件分布如下:
Cluster name: test
Cluster version: v7.1.8-5.2-20250630
Dashboard URL: http://192.168.151.101:2379/dashboard
PD 节点 : 192.168.151.101:2379 (Leader), 103, 104
TiDB 节点: 192.168.151.101:4000, 103, 104
TiKV 节点: 192.168.151.101:20160, 103, 104
---Sysbench 构建测试数据
sysbench oltp_read_write \
--mysql-host=192.168.151.101 \
--mysql-port=4000 \
--mysql-user=root \
--mysql-db=sbtest \
--tables=10 \
--table-size=1000000 \
prepare
TiDB 数据库架构概述
三大核心组件:

| 组件 | 层级 | 状态属性 | 核心职责 |
|---|---|---|---|
| TiDB Server | SQL层 | 无状态 | 负责SQL解析 执行,与PD交互获取信 息 |
| TiKV | 行存储层 | 有状态 | 基于Raft机制,承 载OLTP业务 |
| PD | 调度元数据层 | 有状态 | 集群大脑,负责元 数据存储、TSO生成、集 群调度与高可用 |
| TiFlash | 列存存储层 | 有状态 | 列存存储引擎, 异步从TiKV同步数据, 承载分析类业务 |
TiDB 不采用传统分库分表,而是按 Key 范围将数据自动切分为多个 Region,每个 Region 包含一段连续的 Key 区间。每个 Region 维护 3 个副本,分布在不同 TiKV 节点上,组成一个 Raft Group,通过 Raft 协议保证数据一致性。
考点:Region 是 TiDB 数据调度、副本管理和负载均衡的基本单位,而非表或库。
TiDB Server
TiDB Server 是集群的 SQL 接入层,对外暴露 MySQL 协议端口(默认 4000),本身不存储任何数据,完全无状态,可水平扩展。前端通过 HAProxy、LVS 或 TiProxy 做负载均衡即可实现连接层高可用。
考点:TiDB Server 无状态,意味着任意节点宕机不丢失数据,仅影响该节点上的连接;连接需要在应用层做重试与负载均衡。
--TiDB Server 缓存与内存控制机制:
- 缓存组成与作用: TiDB Server 缓存包含SQL结果缓存、线程缓存、元数据及统计信息缓存,可避免重复访问TiKV获取表结构、重复访问PD获取Region信息
- 核心内存控制参数: 包含server memory limit(控制实例最大内存上限)、会话级内存(默认1G可按需调整)、OOM Action(超内存后默认kill对应SQL,可选仅记录日志),均用于防止OOM

TiKV
TiKV 是 TiDB 的分布式行式存储引擎,承担数据持久化、一致性保障、事务执行与部分计算下推的核心职责。
TiKV 底层使用 RocksDB实现数据持久化。每个 TiKV 节点内部包含两个独立的 RocksDB 实例:
- raft RocksDB:存储 Raft 日志,顺序写入性能高
- kv RocksDB:存储实际的用户数据与 MVCC 版本
将 Raft 日志与业务数据分离存储,是 TiKV 的重要性能优化:Raft 日志是顺序 IO,业务数据是随机 IO,分开存放可减少 IO 竞争,提升写入吞吐。
考点:RocksDB 的写放大、读放大、空间放大是 LSM-Tree 的固有特性,TiKV 通过调优 RocksDB 参数平衡三者关系。
TiKV-Raft
--Raft 一致性协议:
每个 Region 的 3 个副本组成一个 Raft Group,角色分为:
- Leader:唯一处理读写请求的副本,负责日志复制
- Follower:被动接收 Leader 同步的日志,参与选举投票
- Learner:非投票副本,仅用于数据同步
--Raft 组与选举规则:
- Leader 定期向 Follower 发送心跳
- Follower 超时未收到心跳则发起选举,投票选出新 Leader
- 多数派机制保证:少数副本故障不影响集群可用性,3 副本可容忍 1 个节点故障
--Raft 日志复制流程:
- 数据写入先转化为 Raft 日志,经 Leader 本地落盘后同步至 Follower,多数副本落盘完成日志提交,最终应用为 KV 键值对

考点:Raft 写入成功的条件是多数副本持久化日志,而非所有副本都应用到状态机。
TiKV - 分布式事务
--MVCC与分布式事务实现机制
- MVCC核心实现: 通过为同一个key保存多个时间戳版本实现,读取时仅返回读取时刻前已提交的事务版本,实现写操作不阻塞读操作
- 三类列簇存储规则: TiKV包含default、lock、write三类列簇,分别存储业务数据、锁信息、事务提交信息,数据长度小于255字节时存入write列簇,大于等于255字节存入default列簇

--两阶段提交流程
Prewrite 阶段:
- 事务开始时从 PD 获取
start_ts - 选定一个 Key 作为 Primary Key,其余为 Secondary Key
- 对所有要修改的 Key 上锁,写入数据新版本(版本号为 start_ts)
Commit 阶段:
- 从 PD 获取
commit_ts - 提交 Primary Key:清除锁,写入 Write 列记录
- Primary 提交成功后,异步提交所有 Secondary Key
考点:Primary Key 提交成功即代表事务整体成功,Secondary 可异步提交。
--TiKV - 读写路径
- 写入:请求路由到 Region Leader → 走 Raft 日志复制 → 多数派确认 → 应用到 RocksDB
- 读取:默认由 Leader 提供读取服务,通过 MVCC 机制根据事务 start_ts 读取对应版本数据,不阻塞写入
考点:并非所有算子都能下推,例如多表 Join 通常无法下推到 TiKV,需在 TiDB 层完成。
--TiDB分布式事务执行与一致性校验流程

Placement Driver (PD)
PD 是整个 TiDB 集群的 "大脑",自身也通过 Raft 协议组成高可用集群(通常 3 节点),选举出一个 Leader 对外提供服务。
三大核心功能
--功能一:元数据管理
存储集群拓扑信息,包括:
- 每个 Region 的 Key 范围、副本位置、Leader 节点
- TiKV 节点的状态、标签、容量信息
TiDB Server 执行 SQL 时,先向 PD 查询数据所在的 Region 位置,再直接访问对应 TiKV 节点。查询结果会缓存在 TiDB 本地,避免频繁请求 PD。
--功能二:全局 TSO 时间戳
TSO(Timestamp Oracle)是全局单调递增的时间戳服务,为分布式事务提供 start_ts 和 commit_ts,是 MVCC 和事务有序性的基础。
TSO 由物理时间 + 逻辑时间两部分组成,由 PD Leader 统一分配,保证全局唯一且单调递增。
考点:TSO 是分布式事务的核心依赖,PD Leader 宕机期间会短暂无法分配 TSO,新事务无法开启。
--功能三:集群调度
PD 是集群的调度中心,调度以 Region 为单位,目标是实现负载均衡与高可用。
调度总流程:信息收集 → 生成调度 → 执行调度
- 信息收集:TiKV 定期向 PD 发送 Store Heartbeat(节点级)和 Region Heartbeat(Region 级),上报容量、流量、副本状态等信息
- 生成调度:PD 根据调度策略生成 Operator(调度操作)
- 执行调度:PD 将调度指令下发给对应 Region 的 Leader,由 TiKV 自主执行

数据库 SQL 执行流程
以一条 SELECT * FROM users WHERE id = 100; 为例,完整走一遍 SQL 从客户端到结果返回的全链路:
阶段 1:TiDB Server 接收与处理
-
协议接入:客户端通过 MySQL 协议连接 TiDB Server,经过权限校验建立会话
-
SQL 解析:Parser 将 SQL 文本解析为 AST 抽象语法树
-
预处理校验:检查表、列是否存在,做语义校验
-
查询优化:
- 逻辑优化:等价改写、谓词下推
- 物理优化:基于统计信息选择最优执行计划(主键点查走 PointGet 快速路径)
-
定位数据:Executor 通过 PD Client 查询该主键对应的 Region 位置(首次查询,后续走本地缓存)
阶段 2:TiKV 存储层执行
- TiDB 将读取请求发送给对应 Region 的 Leader 节点
- TiKV 根据 MVCC 规则,读取对应 start_ts 版本的数据
- 若有可下推的算子,由 Coprocessor 在本地执行
- TiKV 将结果返回给 TiDB Server
阶段 3:结果汇总返回
- TiDB Server 汇总各分片返回的数据
- 完成无法下推的计算(如 Join、最终聚合、排序)
- 按 MySQL 协议格式将结果返回客户端

考点:点查(PointGet)是 TiDB 的优化快速路径,跳过完整优化流程,直接构建执行计划访问 TiKV,延迟低。
HTAP 架构与 TiFlash 核心特性
- HTAP 业务隔离机制: TiDB 支持通过 TiDB Server 配置物理分隔 OLTP(访问 TiKV)与 OLAP(访问 TiFlash)业务,未手动配置时执行计划会自动选择最优存储引擎访问路径
- TiFlash 核心技术特性: 基于 Raft Learner 角色从 TiKV 异步复制数据,不参与 TiKV 投票选举不影响主业务,通过轻量级 IPC 校验实现与 TiKV 的数据一致性读取

- TiFlash MPP 加速原理: 多 TiFlash 节点可并行执行 join 与聚合操作,通过等值连接字段哈希交换将相同值路由至同一节点本地执行连接,最终汇总结果至 TiDB Server 实现查询加速

- HTAP 典型适用场景: 适配同一份数据同时承载在线交易、实时报表与 BI 分析的混合业务场景
新版本特性
- Placement Rule 数据放置规则: 支持为跨地域集群的 TiKV 节点设置 zone/rack/host 标签,自定义 leader 与 follower 的存放位置、副本数,绑定至表或分区实现业务隔离、异地容灾与访问延迟优化
- 热点小表缓存功能: 将只读或极少修改的高频访问小表全表缓存至 TiDB Server 内存,通过租约机制保证一致性,表数据修改会触发缓存失效,不支持直接对缓存表执行 DDL
- TopSQL 资源诊断功能: 在集群 Dashboard 中展示指定 TiDB/TiKV 实例时间段内 CPU 开销最高的 Top5 正在执行的 SQL,可辅助定位慢查询无法覆盖的高资源占用语句
- TiFlash V7 版本数据落盘机制: V7 版本支持哈希 join、哈希聚集、topn 等算子中间数据落盘至本地磁盘,推荐使用查询级别落盘配置,通过内存阈值参数触发落盘动作以缓解内存不足导致的查询失败问题

核心组件交互实战
查看组件状态
tiup cluster display test

可查看所有实例的状态、角色、版本、部署路径等信息,是日常运维最基础的命令。
PD 元数据观察
使用 PD Leader 节点地址,任意存活 PD 节点均可
查看 PD 成员列表与 Leader 信息
curl http://192.168.151.101:2379/pd/api/v1/members

查看 TiKV Store 信息(容量、状态、Region 数量)
curl http://192.168.151.101:2379/pd/api/v1/stores

查看当前 PD Leader
curl http://192.168.151.101:2379/pd/api/v1/leader

SQL 分裂 Region 与 PD 调度观察
-
确认集群状态
- PD 健康检查:
curl -s http://192.168.151.104:2379/pd/api/v1/health

- 获取当前 Region 总数:
curl -s http://192.168.151.104:2379/pd/api/v1/regions | grep -o '"count":[0-9]*'

- 查看 Store 分布:
curl -s http://192.168.151.104:2379/pd/api/v1/stores | grep -E '"id"|"state_name"|"leader_count"|"region_count"'

根据输出,当前集群状态如下:
| Store ID | 地址 | 状态 | Leader数 | Region数 |
|---|---|---|---|---|
| 1 | 192.168.151.103:20160 | Up | 12 | 25 |
| 2 | 192.168.151.104:20160 | Up | 3 | 25 |
| 7 | 192.168.151.101:20160 | Up | 10 | 25 |
目标
- 对其中一张表执行
SPLIT TABLE,观察 Region 数量变化 - 观察 PD 的
balance-leader-scheduler如何自动均衡 Leader - 理解
SPLIT TABLE对性能的影响
查看 sbtest1 的 Region 详情:
SHOW TABLE sbtest.sbtest1 REGIONS;
REGION_ID START_KEY END_KEY LEADER_ID LEADER_STORE_ID PEERS SCATTERING WRITTEN_BYTES READ_BYTES APPROXIMATE_SIZE(MB) APPROXIMATE_KEYS SCHEDULING_CONSTRAINTS SCHEDULING_STATE 1013 t_132_i_1_0380000000000d181e03800000000002bb3e00 t_135_ 1014 1 1014, 1015, 1016 0 0 0 235 1032715 1009 t_132_ t_132_i_1_0380000000000d181e03800000000002bb3e00 1011 7 1010, 1011, 1012 0 0 0 63 983040
执行分裂并观察变化
-- 将 sbtest1 均匀切分为 16 个 Region
SPLIT TABLE sbtest.sbtest1 BETWEEN (1) AND (1000000) REGIONS 16;

执行后,再次查看表 Region:
SHOW TABLE sbtest.sbtest1 REGIONS;
预期会看到 16 个 Region,大小大致相近
REGION_ID START_KEY END_KEY LEADER_ID LEADER_STORE_ID PEERS SCATTERING WRITTEN_BYTES READ_BYTES APPROXIMATE_SIZE(MB) APPROXIMATE_KEYS SCHEDULING_CONSTRAINTS SCHEDULING_STATE
2,005 t_132_r t_132_r_62500 2,007 7 2006, 2007, 2008 0 0 0 16 92,242
2,009 t_132_r_62500 t_132_r_124999 2,012 2 2010, 2011, 2012 0 0 0 12 51,360
2,013 t_132_r_124999 t_132_r_187498 2,016 2 2014, 2015, 2016 0 0 0 16 68,480
2,017 t_132_r_187498 t_132_r_249997 2,018 1 2018, 2019, 2020 0 0 0 16 68,480
2,021 t_132_r_249997 t_132_r_312496 2,023 7 2022, 2023, 2024 0 0 0 12 51,360
2,025 t_132_r_312496 t_132_r_374995 2,026 1 2026, 2027, 2028 0 0 0 16 68,480
2,029 t_132_r_374995 t_132_r_437494 2,031 7 2030, 2031, 2032 0 0 0 12 51,360
2,033 t_132_r_437494 t_132_r_499993 2,036 2 2034, 2035, 2036 0 0 0 16 68,480
2,037 t_132_r_499993 t_132_r_562492 2,038 1 2038, 2039, 2040 0 0 0 16 68,480
2,041 t_132_r_562492 t_132_r_624991 2,043 7 2042, 2043, 2044 0 0 0 12 51,360
2,045 t_132_r_624991 t_132_r_687490 2,048 2 2046, 2047, 2048 0 39 0 16 68,480
2,049 t_132_r_687490 t_132_r_749989 2,052 2 2050, 2051, 2052 0 39 0 16 68,480
2,053 t_132_r_749989 t_132_r_812488 2,054 1 2054, 2055, 2056 0 0 0 12 51,360
2,057 t_132_r_812488 t_132_r_874987 2,059 7 2058, 2059, 2060 0 39 0 16 68,480
2,061 t_132_r_874987 t_132_r_937486 2,063 7 2062, 2063, 2064 0 27 0 16 68,480
1,013 t_132_r_937486 t_135_ 1,014 1 1014, 1015, 1016 0 2,224 0 15 67,346
1,009 t_132_ t_132_i_1_0380000000000d181e03800000000002bb3e00 1,011 7 1010, 1011, 1012 0 0 0 63 983,040
2,001 t_132_i_1_0380000000000d181e03800000000002bb3e00 t_132_r 2,002 1 2002, 2003, 2004 0 0 0 1 0
观察 PD 的调度过程
分裂后,PD 会自动执行以下调度:
- Region 均衡:
balance-region-scheduler会将新分裂出的 Region 的副本(Peers)分散到不同的 Store,确保每个 Store 的 Region 数量大致相同。 - Leader 均衡:
balance-leader-scheduler会转移 Leader,使每个 Store 的 Leader 数量均衡。
我们可以通过 API 实时观察:
# 查看各 Store 的 Region 数和 Leader 数变化
watch -n 5 'curl -s http://192.168.151.104:2379/pd/api/v1/stores | grep -E "\"id\"|\"leader_count\"|\"region_count\""'
等待几分钟后,各 Store 的 Region 数和 Leader 数应趋于均衡。
"id": 2,
"leader_count": 8,
"region_count": 41,
"id": 7,
"leader_count": 16,
"region_count": 41,
"id": 1,
"leader_count": 17,
"region_count": 41,
TiDB Server
-- 查看 TiDB 版本
SELECT VERSION();

-- 查看所有 TiDB Server 节点信息
SELECT * FROM information_schema.CLUSTER_INFO WHERE TYPE='tidb';

查看 TiDB Server 配置
-- 查看所有实例的配置
SHOW CONFIG WHERE type='tidb';

-- 查看特定配置项(如日志级别)
SHOW CONFIG WHERE type='tidb' AND name LIKE '%log%';

查看 TiDB Server 状态变量
-- 查看全局状态(连接数、查询数等)
SHOW GLOBAL STATUS;

-- 查看当前会话状态
SHOW SESSION STATUS;
查看集群会话信息
-- 查看所有 TiDB Server 上的会话
SELECT instance, user, host, db, time, command, state
FROM information_schema.CLUSTER_PROCESSLIST
WHERE user != 'system user'
ORDER BY time DESC;

查看 SQL 执行计划
-- 使用 EXPLAIN 查看执行计划,理解 TiDB Server 如何将 SQL 下推
EXPLAIN SELECT COUNT(*) FROM sbtest.sbtest1 WHERE k BETWEEN 100 AND 10000;
id estRows task access object operator info StreamAgg_10 1.00 root funcs:count(1)->Column#5 └─IndexReader_15 1.00 root index:IndexRangeScan_14 └─IndexRangeScan_14 1.00 cop[tikv] table:sbtest1, index:k_1(k) range:[100,10000], keep order:false
-- 查看实际执行情况
EXPLAIN ANALYZE SELECT COUNT(*) FROM sbtest.sbtest1 WHERE k BETWEEN 100 AND 10000;
id estRows actRows task access object execution info operator info memory disk StreamAgg_10 1.00 1 root time:5.1ms, loops:2, RU:0.729965 funcs:count(1)->Column#5 388 Bytes N/A └─IndexReader_15 1.00 0 root time:5.1ms, loops:1, cop_task: {num: 1, max: 4.96ms, proc_keys: 0, tot_proc: 764.9µs, tot_wait: 1.73ms, copr_cache_hit_ratio: 0.00, build_task_duration: 1.01ms, max_distsql_concurrency: 1}, rpc_info:{Cop:{num_rpc:1, total_time:4.93ms}} index:IndexRangeScan_14 271 Bytes N/A └─IndexRangeScan_14 1.00 0 cop[tikv] table:sbtest1, index:k_1(k) tikv_task:{time:1ms, loops:1}, scan_detail: {total_keys: 1, get_snapshot_time: 1.6ms, rocksdb: {block: {cache_hit_count: 1, read_count: 4, read_byte: 145.5 KB, read_time: 259.7µs}}}, time_detail: {total_process_time: 764.9µs, total_wait_time: 1.73ms, total_kv_read_wall_time: 1ms, tikv_wall_time: 4.29ms} range:[100,10000], keep order:false N/A N/A
索引使用观测

查看现有索引
-- 查看 sbtest1 表的索引
SHOW INDEX FROM sbtest.sbtest1;
Table Non_unique Key_name Seq_in_index Column_name Collation Cardinality Sub_part Packed Null Index_type Comment Index_comment Visible Expression Clustered Global sbtest1 0 PRIMARY 1 id A 0 [NULL] [NULL] BTREE YES [NULL] YES NO sbtest1 1 k_1 1 k A 176,128 [NULL] [NULL] BTREE YES [NULL] NO NO
使用 EXPLAIN 分析查询
观察 TiDB 如何利用索引执行查询。
-- 查询1:等值查询,预计使用索引 k_1
EXPLAIN SELECT * FROM sbtest.sbtest1 WHERE k = 50000;
id estRows task access object operator info
IndexLookUp_10 5.58 root
├─IndexRangeScan_8(Build) 5.58 cop[tikv] table:sbtest1, index:k_1(k) range:[50000,50000], keep order:false, stats:partial[id:unInitialized]
└─TableRowIDScan_9(Probe) 5.58 cop[tikv] table:sbtest1 keep order:false, stats:partial[id:unInitialized]
-- 查询2:范围查询,可能使用索引
EXPLAIN SELECT * FROM sbtest.sbtest1 WHERE k BETWEEN 10000 AND 20000;
id estRows task access object operator info
IndexLookUp_10 1.00 root
├─IndexRangeScan_8(Build) 1.00 cop[tikv] table:sbtest1, index:k_1(k) range:[10000,20000], keep order:false
└─TableRowIDScan_9(Probe) 1.00 cop[tikv] table:sbtest1 keep order:false
-- 查询3:非索引列查询,会全表扫描
EXPLAIN SELECT * FROM sbtest.sbtest1 WHERE c = 'some string';
id estRows task access object operator info
TableReader_7 1000.00 root data:Selection_6
└─Selection_6 1000.00 cop[tikv] eq(sbtest.sbtest1.c, "some string")
└─TableFullScan_5 1000000.00 cop[tikv] table:sbtest1 keep order:false, stats:partial[c:unInitialized]
输出解读:
sbtest1 表当前有 2 个索引:
| 索引名 | 列 | 类型 | 可见性 | 全局 |
|---|---|---|---|---|
| PRIMARY | id | 主键/聚簇索引 | YES | NO |
| k_1 | k | 普通二级索引 | YES | NO |
Clustered = YES表示PRIMARY是聚簇索引,数据按主键顺序存储,访问主键无需回表。
Global = NO表示这两个索引都是本地索引(非全局索引)。
查询1:等值查询 k = 50000
IndexLookUp_10
├─IndexRangeScan_8 (Build) ← 使用索引 k_1 扫描
└─TableRowIDScan_9 (Probe) ← 回表读取完整行
- TiDB 选择了
k_1索引进行等值查询。 - 执行计划为 IndexLookUp:先在索引中获取符合条件的行 ID,再回表获取完整行。
- 由于
k列不是聚簇索引,回表是必要的。
查询2:范围查询 k BETWEEN 10000 AND 20000
IndexLookUp_10
├─IndexRangeScan_8 (Build) ← 使用索引 k_1 扫描范围
└─TableRowIDScan_9 (Probe) ← 回表读取完整行
- 范围查询同样选择
k_1索引。 - 执行计划与等值查询结构相同(都使用
IndexLookUp)。
查询3:非索引列查询 c = 'some string'
TableReader_7
└─Selection_6
└─TableFullScan_5 ← 全表扫描
c列没有索引,TiDB 进行了全表扫描(1,000,000 行)。- 全表扫描代价高(estRows: 1000.00 → TableFullScan: 1000000.00),反映了优化器的估算逻辑。
-----
使用 information_schema 查看索引使用统计
TiDB 提供了 TIDB_INDEX_USAGE 表,记录索引使用情况(需开启 tidb_enable_index_usage 变量,默认开启)。
-- 查看 sbtest1 各索引的使用次数
SELECT * FROM information_schema.TIDB_INDEX_USAGE
WHERE TABLE_SCHEMA = 'sbtest' AND TABLE_NAME = 'sbtest1';
TABLE_SCHEMA TABLE_NAME INDEX_NAME QUERY_TOTAL KV_REQ_TOTAL ROWS_ACCESS_TOTAL PERCENTAGE_ACCESS_0 PERCENTAGE_ACCESS_0_1 PERCENTAGE_ACCESS_1_10 PERCENTAGE_ACCESS_10_20 PERCENTAGE_ACCESS_20_50 PERCENTAGE_ACCESS_50_100 PERCENTAGE_ACCESS_100 LAST_ACCESS_TIME
sbtest sbtest1 k_1 2 2 0 2 0 0 0 0 0 0 2026-08-07 14:40:42
字段说明:
- QUERY_COUNT:查询该索引的次数
- ROW_ACCESS_COUNT:通过该索引访问的行数
- LAST_USED_AT:最后一次使用时间
1.5 查看慢查询日志 / STATEMENTS_SUMMARY 表
查看慢查询或高频查询中索引的使用情况:
-- 查看最近执行的 SQL 摘要
SELECT DIGEST_TEXT, PLAN, INDEX_NAMES
FROM information_schema.STATEMENTS_SUMMARY
WHERE SCHEMA_NAME = 'sbtest'
ORDER BY SUM_LATENCY DESC LIMIT 10;
分区表与全局索引
-- 创建分区表
DROP TABLE IF EXISTS sbtest.part_table;
CREATE TABLE sbtest.part_table (
id INT NOT NULL,
k INT NOT NULL,
c CHAR(120) NOT NULL,
pad CHAR(60) NOT NULL,
PRIMARY KEY (id, k)
) PARTITION BY RANGE (id) (
PARTITION p0 VALUES LESS THAN (100000),
PARTITION p1 VALUES LESS THAN (200000),
PARTITION p2 VALUES LESS THAN (300000),
PARTITION p3 VALUES LESS THAN (400000)
);
-- 插入测试数据
INSERT INTO sbtest.part_table SELECT * FROM sbtest.sbtest1 WHERE id < 400000;
-- 创建唯一全局索引
CREATE UNIQUE INDEX uk_id_global ON sbtest.part_table (id) GLOBAL;
-- 创建本地索引
CREATE INDEX idx_k_local ON sbtest.part_table (k);
对比查询执行计划
-- 查询1:按分区键 id 过滤
EXPLAIN SELECT * FROM sbtest.part_table WHERE id = 150000;
id estRows task access object operator info
Point_Get_1 1.00 root table:part_table, index:uk_id_global(id)
-- 查询2:按非分区键 k 过滤,使用本地索引
EXPLAIN SELECT * FROM sbtest.part_table WHERE k = 5000;
id estRows task access object operator info
IndexLookUp_10 4.41 root partition:all
├─IndexRangeScan_8(Build) 4.41 cop[tikv] table:part_table, index:idx_k_local(k) range:[5000,5000], keep order:false
└─TableRowIDScan_9(Probe) 4.41 cop[tikv] table:part_table keep order:false
-- 强制使用全局索引
EXPLAIN SELECT /*+ USE_INDEX(sbtest.part_table, idx_k_global) */ *
FROM sbtest.part_table WHERE k = 5000;
id estRows task access object operator info
IndexLookUp_7 4.41 root partition:all
├─IndexRangeScan_5(Build) 4.41 cop[tikv] table:part_table, index:idx_k_local(k) range:[5000,5000], keep order:false
└─TableRowIDScan_6(Probe) 4.41 cop[tikv] table:part_table keep order:false
执行计划对比总览
| 查询编号 | 查询条件 | 使用的索引 | 执行计划类型 | 扫描方式 | 分区扫描 |
|---|---|---|---|---|---|
| 查询1 | id = 150000 | uk_id_global | Point_Get | 直接点查 | 单个分区 |
| 查询2 | k = 5000 | idx_k_local | IndexLookUp | 索引扫描 + 回表 | 全部分区 (partition:all) |
| 查询3 | k = 5000 (强制全局索引) | idx_k_local | IndexLookUp | 索引扫描 + 回表 | 全部分区 (partition:all) |
分区键与索引选择的关系
| 查询条件 | 索引类型 | 是否包含分区键 | 扫描方式 | 性能影响 |
|---|---|---|---|---|
id = 150000 |
全局索引 | 是 | 直接点查 | 最优 |
k = 5000 |
本地索引 | 否 | 扫描所有分区 | 随分区数增加性能下降 |
k = 5000 (强制全局) |
本地索引 | 否 | 扫描所有分区 | Hint 被忽略 |
全局索引限制
- 全局索引的更新代价更高(分布式事务)。
- 不建议在高并发写入场景下频繁使用全局索引。
阶段性学习核心收获
架构原理是PCTA备考的根基,也是分布式数据库与传统单机数据库最本质的分水岭。这段时间的系统学习和实操让我深刻体会到:单纯依赖文档和视频,无法真正应对PCTA考试对原理理解与场景判断的要求——只有亲手搭建集群、反复执行命令、不断观察现象并试错,才能将“听到的知识”转化为“长在手上的能力”。
PCTA考试主要考察架构原理,尤其聚焦于TiDB Server、TiKV、PD三个核心组件的职责边界与协作机制。通过将每一条考试大纲知识点与实操验证一一对应,从“知其然”逐步走向“知其所以然”。这种从现象到原理的复盘过程,不是背结论,而是通过现象推导结论。
目前我的学习仍停留在基础入门阶段,PCTP所要求的多集群管理、性能调优、高阶故障排查等内容仍需深入学习,前路漫长。但我相信,只要保持耐心,坚持“理论铺底、实操验证、问题驱动”的三段式学习节奏,逐步补齐分布式数据库的技术拼图,真正做到学以致用、学有所精。
