编程 TiDB 性能调优:先查 SQL 层,再动参数

2026-09-16 21:31:33

TiDB 性能调优:先查 SQL 层,再动参数

TiDB 调优的经验法则是 80% 的性能问题出在 SQL 层,只有 20% 需要调系统参数。下面按这个取舍组织排查顺序:先 SQL,再 TiDB/TiKV 实例配置,最后操作系统与集群侧参数。

一、性能调优方法论

1.1 调优的基本原则

调优不是一上来就改参数,而是遵循以下步骤:

  1. 建立基线 → 压测当前配置,记录性能指标
  2. 识别瓶颈 → 找到系统的短板(CPU/内存/磁盘/网络/SQL)
  3. 制定方案 → 针对性地优化
  4. 验证效果 → 再次压测,对比指标
  5. 持续迭代 → 调优是一个持续过程

1.2 性能瓶颈的层次

应用层
├── SQL 质量 (影响最大)
│    ├── 索引是否合理
│    ├── JOIN 方式是否正确
│    └── 是否有不必要的全表扫描
数据库层
├── TiDB 配置(内存限制、并发度、事务模式)
├── TiKV 配置(Block Cache 大小、线程池大小、Raft Store 配置)
└── PD 配置(调度频率、TSO 批量大小)
系统层
├── CPU、内存、磁盘 I/O (最关键)、网络

二、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)
  • 元数据缓存(表结构、统计信息)
  • 其他内部结构

控制连接内存: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
SET GLOBAL tidb_prepared_plan_cache_size = 100;  -- 缓存 100 个计划

-- 开启 Non-Prepared Plan Cache(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 调优(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
16GB8GB
32GB16GB
64GB32GB
128GB48GB

原则:Block Cache 约占 TiKV 可用内存的 45%–50%。

4.2 线程池调优

tikv_servers:
- host: 10.0.1.21
config:
server.grpc-concurrency: 8                  # gRPC 并发线程数(默认 8)
storage.scheduler-worker-pool-size: 4       # 调度线程池大小(默认 4)
readpool.coprocessor.normal-concurrency: 8  # 协处理器线程池(默认 8)
raftstore.store-pool-size: 2
raftstore.apply-pool-size: 2

调优建议:

CPU 核数grpc-concurrencycoprocessorscheduler
8 核663
16 核884
32 核+12126

4.3 Raft KV 引擎优化

storage.engine: "raft-kv" 为默认值,基于 RocksDB;TiDB 8.0+ 支持分区 Raft KV 引擎,适用于高并发场景,可减少写入放大。

rocksdb.max-open-files: 1024
rocksdb.max-background-jobs: 8

引擎选择建议:

  • 通用场景:raft-kv(平衡读写)
  • 高并发写入:raft-kv + 增大后台线程
  • 读密集:raft-kv + 增大 Block Cache

RocksDB 调优:

rocksdb.defaultcf.block-cache-size: "8GB"
rocksdb.writecf.block-cache-size: "2GB"
rocksdb.lockcf.block-cache-size: "512MB"

五、操作系统层调优

5.1 CPU 调度

echo performance > /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor

# 绑定 TiKV 进程到特定 CPU 核心(减少 Cache Miss)
taskset -c 0-15 tikv-server ...

5.2 磁盘 I/O 调优

cat /sys/block/nvme0n1/queue/scheduler
echo none > /sys/block/nvme0n1/queue/scheduler  # NVMe SSD 推荐
echo 1024 > /sys/block/nvme0n1/queue/nr_requests

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"
sysctl -w net.ipv4.tcp_fastopen=3
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 定期清理。safe_point 之后最老的版本被保留。

6.2 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 天)
UPDATE mysql.tidb SET variable_value = '24h' WHERE variable_name = 'tikv_gc_life_time';

6.3 GC 调优建议

  • 存储压力大 → 缩短 tikv_gc_life_time(如 24h)
  • 长事务多 → 延长(如 72h)
  • 频繁 Stale Read → 延长
  • 写入量大 → 缩短 tikv_gc_run_interval(如 5m)

七、Region 调优

7.1 Region 大小

默认 Region 96MB。注意 SHOW CONFIG 不支持 LIMIT,需要用 head 过滤输出。

SHOW CONFIG WHERE name LIKE '%split%';

7.2 预分裂 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 调度

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 资源组

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 版本而异,请参考官方文档。

九、性能压测

9.1 Sysbench

yum install sysbench -y

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

9.2 TPC-C(更贴近真实业务场景)

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

压测后调优

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

补充几点实战经验:

  • 先用 EXPLAIN ANALYZE 看实际执行耗时,比单纯 EXPLAIN 更准。
  • 发现 TableFullScan 但加索引没效果,可能是数据分布问题,试试 ANALYZE TABLE 更新统计信息。
  • TiKV 调优可优先调 block-cache 大小,默认约总内存 45%,业务高并发时可调到 60%–70%。
复制全文 生成海报 TiDB 数据库调优 TiKV SQL优化 运维

推荐文章

程序员茄子在线接单