一、性能调优方法论
1.1 调优的基本原则
调优不是一上来就改参数,而是遵循以下步骤:
1. 建立基线 → 压测当前配置,记录性能指标
2. 识别瓶颈 → 找到系统的短板(CPU/内存/磁盘/网络/SQL)
3. 制定方案 → 针对性地优化
4. 验证效果 → 再次压测,对比指标
5. 持续迭代 → 调优是一个持续过程
1.2 性能瓶颈的层次
应用层
│
├── SQL 质量 (影响最大)
│ ├── 索引是否合理
│ ├── JOIN 方式是否正确
│ └── 是否有不必要的全表扫描
│
数据库层
│
├── TiDB 配置
│ ├── 内存限制
│ ├── 并发度
│ └── 事务模式
│
├── TiKV 配置
│ ├── Block Cache 大小
│ ├── 线程池大小
│ └── Raft Store 配置
│
└── PD 配置
├── 调度频率
└── TSO 批量大小
│
系统层
│
├── CPU
├── 内存
├── 磁盘 I/O (最关键)
└── 网络
经验法则:80% 的性能问题出在 SQL 层面,只有 20% 需要调整系统参数。
二、SQL 层调优
2.1 索引调优
添加缺失索引:
-- 查看执行计划,找到全表扫描
EXPLAIN SELECT * FROM orders WHERE user_id = 12345;
-- 如果是 TableFullScan,添加索引
CREATE INDEX idx_user_id ON orders(user_id);
删除未使用索引:
-- TiDB 6.0+ 可以查看所有索引列表
SELECT
TABLE_SCHEMA,
TABLE_NAME,
KEY_NAME, -- 注意: 是 KEY_NAME 不是 INDEX_NAME
COLUMN_NAME
FROM information_schema.tidb_indexes
WHERE TABLE_SCHEMA = 'your_db';
-- 结合 information_schema.statements_summary 或慢查询日志判断索引是否被使用
-- 如果某索引在 EXPLAIN 中从未被选中,考虑删除
DROP INDEX idx_unused ON orders;
复合索引设计:
-- 场景: 经常按 (status, created_at) 查询,且经常按 created_at 排序
SELECT * FROM orders WHERE status = 1 ORDER BY created_at DESC LIMIT 20;
-- 设计索引时,等值条件列在前,排序列在后
CREATE INDEX idx_status_created ON orders(status, created_at);
-- 这样既可以用索引过滤 status,又可以用索引避免排序
2.2 JOIN 调优
选择合适的 Join 类型:
| Join 类型 | 适用场景 |
|---|---|
| HashJoin | 两表都无合适索引,数据量中等 |
| IndexJoin | 内表有索引,外表数据量小 |
| MergeJoin | 两边都有序(有索引) |
-- 强制使用 IndexJoin(当优化器选择不当时)
SELECT /*+ INL_JOIN(orders) */ *
FROM users
JOIN orders ON users.id = orders.user_id
WHERE users.id = 123;
-- 强制使用 HashJoin
SELECT /*+ HASH_JOIN(users, orders) */ *
FROM users
JOIN orders ON users.id = orders.user_id
WHERE users.status = 1;
控制 Join 的驱动表:
-- 让小表驱动大表
-- TiDB 优化器通常会自动选择,但可以通过 Hint 强制
SELECT /*+ TIDB_INLJ(small_table) */ *
FROM small_table
JOIN big_table ON small_table.id = big_table.small_id;
2.3 子查询优化
-- 差的相关子查询(每行都执行一次子查询)
SELECT name FROM users
WHERE id IN (SELECT user_id FROM orders WHERE amount > 1000);
-- 好的方式(改写为 JOIN)
SELECT DISTINCT u.name FROM users u
JOIN orders o ON u.id = o.user_id
WHERE o.amount > 1000;
三、TiDB Server 调优
3.1 内存调优
-- 单查询内存配额(默认 1GB)
SET GLOBAL tidb_mem_quota_query = 1073741824;
-- 如果查询经常 OOM,降低该值
SET GLOBAL tidb_mem_quota_query = 536870912; -- 512MB
-- 如果查询被误杀(OOM 但实际不需要那么多内存),提高该值
SET GLOBAL tidb_mem_quota_query = 2147483648; -- 2GB
内存使用构成:
TiDB Server 内存使用:
├── SQL 执行内存 (受 tidb_mem_quota_query 控制)
├── 连接内存 (每个连接 ~10MB)
├── 元数据缓存 (表结构、统计信息)
└── 其他内部结构
控制连接内存:
-- 限制最大连接数
-- 在 TiDB 配置中设置
-- max_connections = 0 (默认值,表示不限制)
-- 生产环境建议设置为 5000-10000
3.2 并发度调优
-- 控制 SQL 层并发度
SET GLOBAL tidb_distsql_scan_concurrency = 15; -- 默认 15
-- 增大该值可以提升扫描并发,但也会增加 TiKV 压力
-- 控制 Index Lookup Join 并发度
SET GLOBAL tidb_index_lookup_join_concurrency = 4; -- 默认 4
3.3 执行计划缓存
-- 开启 Prepared Plan Cache(Prepared 语句的执行计划缓存)
SET GLOBAL tidb_prepared_plan_cache_size = 100; -- 缓存 100 个计划
-- 开启 Non-Prepared Plan Cache(普通 SQL 的执行计划缓存,TiDB 7.0+)
SET GLOBAL tidb_enable_non_prepared_plan_cache = ON;
SET GLOBAL tidb_non_prepared_plan_cache_size = 100;
执行计划缓存适合:
- 参数化查询(WHERE id = ? 形式的查询)
- 相同结构、不同参数值的 SQL
四、TiKV 调优
4.1 Block Cache 调优
Block Cache 是 TiKV 最重要的缓存机制:
# 通过 TiUP 修改配置
# tiup cluster edit-config tidb-cluster
tikv_servers:
- host: 10.0.1.21
config:
storage.block-cache.capacity: "16GB" # 根据内存大小调整
Block Cache 大小建议:
| TiKV 内存 | Block Cache 建议 |
|---|---|
| 16GB | 8GB |
| 32GB | 16GB |
| 64GB | 32GB |
| 128GB | 48GB |
原则:Block Cache 约占 TiKV 可用内存的 45%-50%。
4.2 线程池调优
tikv_servers:
- host: 10.0.1.21
config:
# gRPC 并发线程数(默认 8)
server.grpc-concurrency: 8
# 调度线程池大小(默认 4)
storage.scheduler-worker-pool-size: 4
# 协处理器线程池(默认 8)
readpool.coprocessor.normal-concurrency: 8
# Raft Store 线程数
raftstore.store-pool-size: 2
raftstore.apply-pool-size: 2
调优建议:
| CPU 核心数 | grpc-concurrency | coprocessor | scheduler |
|---|---|---|---|
| 8 核 | 6 | 6 | 3 |
| 16 核 | 8 | 8 | 4 |
| 32 核+ | 12 | 12 | 6 |
4.3 Raft KV 引擎优化
TiKV 支持多种存储引擎,选择合适的引擎可以提升性能:
tikv_servers:
- host: 10.0.1.21
config:
# 默认引擎: raft-kv (基于 RocksDB)
# TiDB 8.0+ 支持分区 Raft KV 引擎
# 适用于高并发场景,可以减少写入放大
storage.engine: "raft-kv"
# RocksDB 优化
rocksdb.max-open-files: 1024
rocksdb.max-background-jobs: 8
引擎选择建议:
| 场景 | 推荐引擎 | 说明 |
|---|---|---|
| 通用场景 | raft-kv (默认) | 平衡读写性能 |
| 高并发写入 | raft-kv + 增大后台线程 | 减少写入队列等待 |
| 读密集 | raft-kv + 增大 Block Cache | 提高缓存命中率 |
RocksDB 调优:
tikv_servers:
- host: 10.0.1.21
config:
rocksdb.defaultcf.block-cache-size: "8GB"
rocksdb.writecf.block-cache-size: "2GB"
rocksdb.lockcf.block-cache-size: "512MB"
五、操作系统层调优
5.1 CPU 调度
# 设置 CPU 调度策略(减少上下文切换)
echo performance > /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
# 绑定 TiKV 进程到特定 CPU 核心(减少 Cache Miss)
# 使用 taskset 或 numactl
taskset -c 0-15 tikv-server ... # 绑定到 0-15 核心
5.2 磁盘 I/O 调优
# 检查磁盘调度算法
cat /sys/block/nvme0n1/queue/scheduler
# 设置为 none(NVMe SSD 推荐)
echo none > /sys/block/nvme0n1/queue/scheduler
# 设置 I/O 队列深度
echo 1024 > /sys/block/nvme0n1/queue/nr_requests
# 检查 I/O 调度器的 Read-Ahead
blockdev --getra /dev/nvme0n1
blockdev --setra 256 /dev/nvme0n1 # 256 * 512 = 128KB
5.3 网络调优
# 增加网络缓冲区
sysctl -w net.core.rmem_max=16777216
sysctl -w net.core.wmem_max=16777216
sysctl -w net.ipv4.tcp_rmem="4096 87380 16777216"
sysctl -w net.ipv4.tcp_wmem="4096 65536 16777216"
# 开启 TCP 快速确认
sysctl -w net.ipv4.tcp_fastopen=3
# 增加 TCP 连接追踪表大小
sysctl -w net.netfilter.nf_conntrack_max=1048576
5.4 文件系统调优
# ext4 推荐选项(/etc/fstab)
# /dev/nvme0n1 /tidb-data ext4 defaults,noatime,nodiratime,discard 0 2
# 挂载后验证
mount | grep tidb-data
# 检查文件系统对齐
cat /sys/block/nvme0n1/alignment_offset
# 应该为 0
六、垃圾回收(GC)调优
6.1 GC 机制简述
TiDB 使用 MVCC,旧版本数据不会被立即删除,而是由 GC 定期清理:
时间线:
T1: 写入 v1 (TS=100)
T2: 写入 v2 (TS=200)
T3: 写入 v3 (TS=300)
GC 之前:
Key: t56_r100
→ v1 (TS=100) ← 可能被旧事务需要
→ v2 (TS=200) ← 可能被旧事务需要
→ v3 (TS=300) ← 最新版本
GC 之后 (safe_point=250):
Key: t56_r100
→ v2 (TS=200) ← safe_point 之后最老的版本
→ v3 (TS=300) ← 最新版本
(v1 被清理)
6.2 GC 配置
-- 查看 GC 配置
SELECT * FROM mysql.tidb WHERE variable_name LIKE 'tikv_gc%';
-- 设置 GC 间隔(默认 10 分钟)
UPDATE mysql.tidb SET variable_value = '10m'
WHERE variable_name = 'tikv_gc_run_interval';
-- 设置数据保留时间(默认 168 小时 = 7 天)
-- 缩短可以更快释放空间,但会影响长事务和 Stale Read
UPDATE mysql.tidb SET variable_value = '24h'
WHERE variable_name = 'tikv_gc_life_time';
6.3 GC 调优建议
| 场景 | 建议 |
|---|---|
| 存储压力大 | 缩短 tikv_gc_life_time(如 24h) |
| 长事务多 | 延长 tikv_gc_life_time(如 72h) |
| 频繁 Stale Read | 延长 tikv_gc_life_time |
| 写入量大 | 缩短 tikv_gc_run_interval(如 5m) |
七、Region 调优
7.1 Region 大小调优
-- 查看 Region 分裂阈值
-- 注意: SHOW CONFIG 不支持 LIMIT,需要用 head 过滤输出
SHOW CONFIG WHERE name LIKE '%split%';
-- 默认 Region 大小为 96MB
-- 如果单行数据很大,可以减少分裂阈值,让 Region 更小
7.2 预分裂 Region
对于即将导入大量数据的表,可以预先分裂 Region:
-- 按主键范围预分裂
CREATE TABLE big_table (
id BIGINT PRIMARY KEY,
data VARCHAR(255)
) PRE_SPLIT_REGIONS = 16;
-- 或者在创建后手动分裂
SPLIT TABLE big_table BETWEEN (0) AND (1000000000) REGIONS 16;
-- 按索引分裂
SPLIT TABLE big_table INDEX idx_name BETWEEN ('A') AND ('Z') REGIONS 26;
预分裂的好处:
- 避免写入集中在一个 Region(热点)
- 导入时可以直接写入到多个 Region
7.3 Region 调度
-- 查看 PD 调度配置
SHOW CONFIG WHERE type = 'pd' AND name LIKE '%schedule%';
-- 调整 Region 迁移速度(默认较慢)
curl -X POST http://10.0.1.11:2379/pd/api/v1/config \
-d '{"schedule.region-schedule-limit": 2048}'
-- 加快 Leader 迁移
curl -X POST http://10.0.1.11:2379/pd/api/v1/config \
-d '{"schedule.leader-schedule-limit": 8}'
八、负载调优(Resource Control)
8.1 资源组
TiDB 支持资源管控,可以为不同业务设置资源上限:
-- 创建资源组
CREATE RESOURCE GROUP rg_critical RU_PER_SEC = 10000;
CREATE RESOURCE GROUP rg_normal RU_PER_SEC = 5000;
CREATE RESOURCE GROUP rg_low RU_PER_SEC = 1000;
-- 将用户绑定到资源组
-- 注意:需要先创建用户
CREATE USER 'analyst'@'%';
ALTER USER 'analyst'@'%' RESOURCE GROUP rg_low;
-- 将特定查询绑定到资源组
SELECT /*+ RESOURCE_GROUP(rg_critical) */ * FROM critical_table;
8.2 runaway Queries 管控
自动终止消耗资源过多的查询:
-- 在资源组上配置 runaway 规则,自动终止消耗资源过多的查询
-- 具体语法因 TiDB 版本而异,请参考官方文档
-- 示例(TiDB 8.0+):
-- ALTER RESOURCE GROUP rg_normal
-- QUERY_LIMIT (ACTION = KILL, ELAPSED = 60);
九、性能压测
9.1 使用 Sysbench 压测
# 安装 Sysbench
yum install sysbench -y # 或 apt install sysbench
# 准备数据
sysbench oltp_read_write \
--mysql-host=10.0.1.14 \
--mysql-port=4000 \
--mysql-user=root \
--mysql-db=sbtest \
--tables=10 \
--table-size=1000000 \
prepare
# 执行压测
sysbench oltp_read_write \
--mysql-host=10.0.1.14 \
--mysql-port=4000 \
--mysql-user=root \
--mysql-db=sbtest \
--threads=32 \
--time=300 \
--report-interval=10 \
run
输出示例:
[ 10s ] thds: 32 tps: 2345.67 qps: 46913.40 (r/w/o: 32839.68/9382.72/4691.00)
[ 20s ] thds: 32 tps: 2456.78 qps: 49135.60 (r/w/o: 34394.92/9827.12/4913.56)
...
SQL statistics:
queries performed:
read: 9800000
write: 2800000
total: 12600000
transactions: 700000 (2333.33 per sec.)
queries: 12600000 (41999.94 per sec.)
9.2 使用 TPC-C 压测
TPC-C 是更贴近真实业务场景的压测标准:
# 使用 TiDB 官方提供的 TPC-C 工具
# https://github.com/pingcap/go-tpc
# 准备数据
go-tpc tpcc prepare --host 10.0.1.14 --port 4000 --warehouses 100
# 执行压测
go-tpc tpcc run --host 10.0.1.14 --port 4000 --warehouses 100 --threads 32
十、调优检查清单
部署后必做
- [ ]确认操作系统参数已优化(Swap、THP、文件描述符)
- [ ]确认磁盘对齐和 I/O 调度器正确
- [ ]设置合理的 Block Cache 大小
- [ ]开启慢查询日志
- [ ]配置告警规则
日常调优
- [ ]定期 ANALYZE TABLE 更新统计信息
- [ ]检查未使用的索引
- [ ]分析慢查询,优化执行计划
- [ ]监控 Region 分布,处理热点
- [ ]检查 GC 配置是否合理
压测后调优
- [ ]对比压测结果与预期目标
- [ ]根据瓶颈调整配置
- [ ]重新压测验证效果
- [ ]记录调优前后的对比数据