0
0
0
0
博客/.../

TiDB 性能调优最佳实践

 北南南北  发表于  2026-08-03

一、性能调优方法论

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 配置是否合理

压测后调优

  • [ ]对比压测结果与预期目标
  • [ ]根据瓶颈调整配置
  • [ ]重新压测验证效果
  • [ ]记录调优前后的对比数据

0
0
0
0

版权声明:本文为 TiDB 社区用户原创文章,遵循 CC BY-NC-SA 4.0 版权协议,转载请附上原文出处链接和本声明。

评论
暂无评论