TiDB 常用运维指令手册

运维指令手册 44.5 KB 原文

TiDB 常用运维指令手册

适用版本:TiDB v6.5 / v7.5 / v8.1 / v8.5 LTS(当前推荐 v8.5) 约定:# root 或 sudo,$ 普通用户,tidb> MySQL 协议客户端提示符,<xxx> 为占位符。 差异标注:正文用 > **版本差异** 引用块标注跨版本不一致之处。 更新时间:2026-11


目录

  1. 版本与支持周期
  2. 架构与组件职责
  3. 下载与安装
  4. 集群部署(TiUP)
  5. 集群日常运维
  6. 连接与账号权限
  7. 关键参数与配置
  8. 库表对象管理与 TiDB 限制
  9. 备份与恢复(BR)
  10. 日志备份与 PITR
  11. 数据导入导出(Dumpling / Lightning)
  12. 数据同步(TiCDC / DM)
  13. 高可用与 PD 调度
  14. 日志与监控体系
  15. 性能诊断
  16. GC 与空间管理
  17. 扩缩容与版本升级
  18. 高频故障速查
  19. 危险命令与避坑清单
  20. 附录:一键巡检脚本

1. 版本与支持周期

1.1 发布节奏

TiDB 采用「X.Y.Z」三段式:X 每年递增一次,Y 为系列号,Z 为补丁号。LTS 大约每半年发一个,DMR(Development Milestone Release)每两个月一个且不带 -DMR 后缀的不能用于生产(PingCAP 对 DMR 不提供支持)。

版本 类型 状态(2026-11) 说明
v6.1 LTS 已 EOL 不建议新部署
v6.5 LTS 延保期 存量较多,建议规划升级
v7.1 LTS 延保期 —
v7.5 LTS 维护中 长期存量版本
v8.1 LTS 维护中 —
v8.5 LTS 当前推荐 社区支持至 2029-12-19,企业版维护+延保至 2030

关键判断:PingCAP 在支持策略页已明确 v8.5 是这一代产品线的最后一个 LTS,后续将基于同一开源底座推出新一代产品,并承诺提供从 v8.5 升级的路径。这意味着:

  1. 现在新上生产,选 v8.5 LTS 是唯一稳妥选择;
  2. 不要把 v8.5 当作「还能用五年不用管」的版本,升级窗口需要按厂商路线图提前排期;
  3. 招标/立项文档里写版本时,建议写「v8.5 LTS 及以上」,避免被锁死在单一版本。

1.2 版本识别

# 集群维度(推荐,能一次看到所有组件版本)
tiup cluster display <cluster-name>

# SQL 维度
mysql -h 127.0.0.1 -P 4000 -u root -p -e "SELECT tidb_version()\G"

# 单组件二进制
tidb-server -V
tikv-server -V
pd-server -V
-- 各组件版本汇总(需开启集群信息表)
SELECT TYPE, INSTANCE, STATUS_ADDRESS, VERSION, GIT_HASH, START_TIME, UPTIME
FROM information_schema.cluster_info;

1.3 与 MySQL 的兼容边界

这是运维 TiDB 第一件要建立的心智:它兼容的是 MySQL 的协议和大部分语法,不是 MySQL 的全部行为。

能力 TiDB 支持情况 运维影响
外键 v6.6 起实验性支持,默认 foreign_key_checks=OFF 时不生效 从 MySQL 迁入的应用若依赖外键约束,需改为应用层保证
存储过程 / 函数 不支持存储过程、触发器、事件 迁移前必须清点 SHOW PROCEDURE STATUS
视图 支持读写视图,不支持 WITH CHECK OPTION 的部分语义 一般可用
自增主键 支持,但不保证连续,多 TiDB 节点各自预分配 不能拿自增 ID 做业务序号
事务大小 单事务默认上限 100 MiB(txn-total-size-limit) 大批量 DELETE/INSERT 必须分批
隔离级别 默认 REPEATABLE-READ,实际为快照隔离 不会出现幻读,但也没有 MySQL 的间隙锁行为
SELECT ... FOR UPDATE 支持,悲观锁模式默认开启 —
字符集 默认 utf8mb4,utf8mb4_bin / utf8mb4_general_ci 可用 老 MySQL 的 utf8 会映射为 utf8mb4
AUTO_INCREMENT 步长 由 auto_increment_increment 控制,多节点不共享 分库分表场景要显式设置
-- 迁移前的兼容性摸底:找出存储过程/触发器/事件
SELECT ROUTINE_SCHEMA, ROUTINE_NAME, ROUTINE_TYPE FROM information_schema.ROUTINES;
SELECT TRIGGER_SCHEMA, TRIGGER_NAME FROM information_schema.TRIGGERS;
SELECT EVENT_SCHEMA, EVENT_NAME FROM information_schema.EVENTS;

2. 架构与组件职责

理解组件边界,才知道故障该看谁。

        应用(MySQL 协议)
               │
        ┌──────▼──────┐
        │  TiDB Server │  无状态 SQL 层,可水平扩展
        └──────┬──────┘
               │ gRPC
     ┌─────────┴─────────┐
     │                   │
┌────▼────┐        ┌─────▼─────┐
│   TiKV   │        │  TiFlash  │  列存副本,走 Raft Learner
│ 行存 KV  │        │  列存分析  │
└────┬────┘        └───────────┘
     │
┌────▼────┐
│   PD     │  元数据、TSO、调度中心(必须奇数个)
└─────────┘

辅助:TiCDC(增量同步)、TiDB Lightning(导入)、Dumpling(导出)、BR(备份)、TiUP(部署运维)
组件 默认端口 有状态 故障影响
TiDB 4000(SQL)/ 10080(状态) 否 单节点故障仅影响该节点上的连接,客户端重连即可
TiKV 20160 是 副本所在节点故障,Region Leader 重新选举,短暂抖动
PD 2379(客户端)/ 2380(对等) 是 集群失去 TSO 分配能力,全局不可写
TiFlash 9000(内部)/ 8123(HTTP) 是 只影响列存查询,可回退 TiKV
TiCDC 8300 否 同步中断,源库不受影响
Grafana 3000 否 仅影响观测
Alertmanager 9093 否 仅影响告警

红线:PD 必须部署 3 个或 5 个,且不能和 TiKV 混部在同一块物理盘上。PD 集群失去多数派 = 整个集群停写,这是 TiDB 唯一无法通过加 TiDB 节点缓解的单点。

2.1 关键概念

概念 含义 运维关注点
Region TiKV 数据分片单位,默认 96 MiB Region 过多会推高 PD 内存与心跳压力
Leader / Follower Region 的 Raft 角色 Leader 分布不均会导致单节点热点
Store 一个 TiKV 实例 store status 为 Up/Offline/Tombstone
TSO 全局时间戳,由 PD 分配 PD 抖动会直接表现为 SQL 延迟升高
Placement Rule 副本放置策略 跨机房、跨城容灾靠它实现
GC 回收历史版本 MVCC 数据 safe_point 决定能恢复到多早

3. 下载与安装

3.1 官方入口

用途 地址
官网下载中心 https://cn.pingcap.com/product-community/
TiDB 文档 https://docs.pingcap.com/zh/tidb/stable
GitHub Releases https://github.com/pingcap/tidb/releases
TiUP 镜像 https://tiup-mirrors.pingcap.com
Docker Hub https://hub.docker.com/r/pingcap/tidb
离线镜像打包文档 https://docs.pingcap.com/zh/tidb/stable/tiup-mirror

国内网络:默认走 tiup-mirrors.pingcap.com,若拉取超时,可设置 TIUP_MIRRORS=https://mirrors.tuna.tsinghua.edu.cn/tiup 或使用私有离线镜像。

3.2 安装 TiUP(唯一推荐的运维入口)

# 在线安装
curl --proto '=https' --tlsv1.2 -sSf https://tiup-mirrors.pingcap.com/install.sh | sh

# 使环境变量生效(安装脚本会提示具体 profile 路径)
source ~/.bashrc

# 验证
tiup --version
# 离线环境:先在有网机器打包镜像,再拷到内网
# 有网机器
tiup mirror clone tidb-offline-v8.5 linux/amd64 --os=linux --arch=amd64 -v v8.5.3
tar czf tidb-offline-v8.5.tar.gz tidb-offline-v8.5

# 内网机器
tar xzf tidb-offline-v8.5.tar.gz
cd tidb-offline-v8.5 && sh local_install.sh
# 按提示执行 source 使 tiup 指向本地镜像

3.3 常用组件包

tiup list                 # 列出可用组件
tiup list tidb            # 列出 tidb 的可安装版本
tiup install tidb:v8.5.3 tikv:v8.5.3 pd:v8.5.3 tiflash:v8.5.3
tiup install br:v8.5.3 dumpling:v8.5.3 tidb-lightning:v8.5.3 cdc:v8.5.3
tiup update --all         # 升级所有已安装组件
tiup uninstall tidb --all # 卸载某组件所有版本

3.4 单机试用(严禁用于生产)

# 一条命令拉起 1 TiDB + 1 TiKV + 1 PD + 1 TiFlash
tiup playground v8.5.3

# 指定实例数与是否启用 TiFlash
tiup playground v8.5.3 --db 2 --pd 3 --kv 3 --tiflash 1

# 数据落盘(默认在临时目录,重启即丢)
tiup playground v8.5.3 --tag demo --db 1 --pd 1 --kv 1

# 销毁
tiup playground --tag demo --kill

playground 是单进程模拟,没有真实的 Raft 多副本,任何压测结论都不成立。只用于验证语法和功能。


4. 集群部署(TiUP)

4.1 环境预检

# 生成模板拓扑文件
tiup cluster template --full > topology.yaml

# 对目标机做部署前检查(SSH、CPU、内存、磁盘、时钟同步、内核参数)
tiup cluster check topology.yaml --user root -p
tiup cluster check topology.yaml --user root -p --apply   # 自动修复可修复项

# 仅检查操作系统层面
tiup cluster check topology.yaml --cluster --user root -p

预检不通过的典型项:

检查项 要求 修复
cpu-governor 建议 performance cpupower frequency-set -g performance
memory TiKV 节点 ≥ 8 GB,推荐 32 GB+ —
disk 数据盘不能是 ext4 且无 nodelalloc 挂载参数加 nodelalloc,noatime
selinux 需 disabled setenforce 0 + 改 /etc/selinux/config
thp 需 never echo never > /sys/kernel/mm/transparent_hugepage/enabled
swap 需关闭 swapoff -a + 注释 /etc/fstab
ntp 时间偏差 < 500 ms 配 chronyd

TiKV 对磁盘延迟极其敏感。不要用网络存储(NFS/CephFS)挂 TiKV 数据目录,也不要把多个 TiKV 实例的数据目录放同一块盘。

4.2 最小生产拓扑(3 节点混部)

# topology.yaml
global:
  user: "tidb"
  ssh_port: 22
  deploy_dir: "/tidb-deploy"
  data_dir: "/tidb-data"
  arch: "amd64"

server_configs:
  tidb:
    log.slow-threshold: 300
    mem-quota-query: 8589934592          # 单条 SQL 内存上限 8 GB
    oom-action: "cancel"
    performance.max-procs: 0             # 0 = 用满 CPU
  tikv:
    readpool.unified.max-thread-count: 0
    storage.block-cache.capacity: "16GB" # 建议为物理内存的 45%
    raftstore.apply-pool-size: 4
  pd:
    schedule.max-merge-region-size: 20
    schedule.max-merge-region-rows: 200000
  tiflash:
    profiles.default.max_memory_usage: 17179869184

pd_servers:
  - host: 10.0.0.11
  - host: 10.0.0.12
  - host: 10.0.0.13

tidb_servers:
  - host: 10.0.0.21
  - host: 10.0.0.22

tikv_servers:
  - host: 10.0.0.31
    port: 20160
    status_port: 20180
    config:
      server.grpc-concurrency: 8
  - host: 10.0.0.32
    port: 20160
    status_port: 20180
  - host: 10.0.0.33
    port: 20160
    status_port: 20180

tiflash_servers:
  - host: 10.0.0.41
    data_dir: "/tidb-data/tiflash-9000"

monitoring_servers:
  - host: 10.0.0.51

grafana_servers:
  - host: 10.0.0.51

alertmanager_servers:
  - host: 10.0.0.51

4.3 部署与启停

# 部署(只下发二进制和配置,不启动)
tiup cluster deploy <cluster-name> v8.5.3 topology.yaml --user root -p

# 启动
tiup cluster start <cluster-name>
tiup cluster start <cluster-name> -R tikv        # 只启动某类角色
tiup cluster start <cluster-name> -N 10.0.0.31   # 只启动某个节点

# 停止(注意:停 TiKV 会触发 Region 迁移)
tiup cluster stop <cluster-name>
tiup cluster stop <cluster-name> -R tidb

# 重启
tiup cluster restart <cluster-name>
tiup cluster restart <cluster-name> -R tidb -N 10.0.0.21

# 查看
tiup cluster list
tiup cluster display <cluster-name>
tiup cluster display <cluster-name> --dashboard   # 输出 Dashboard 地址

# 彻底销毁(不可恢复,需二次确认)
tiup cluster destroy <cluster-name>

停机顺序:TiDB → TiKV → PD。启动顺序:PD → TiKV → TiDB。 反过来停会触发大量 Region 重新选举和无意义的副本补全,几十 TB 的集群可能因此跑几小时。

4.4 无 TiUP 的裸机启动(应急排查用)

# PD
pd-server --name=pd-1 --data-dir=/data/pd --client-urls=http://0.0.0.0:2379 \
  --peer-urls=http://0.0.0.0:2380 --initial-cluster="pd-1=http://10.0.0.11:2380"

# TiKV
tikv-server --addr=0.0.0.0:20160 --advertise-addr=10.0.0.31:20160 \
  --status-addr=0.0.0.0:20180 --data-dir=/data/tikv \
  --pd-endpoints=10.0.0.11:2379,10.0.0.12:2379,10.0.0.13:2379

# TiDB
tidb-server --store=tikv --path=10.0.0.11:2379 \
  --advertise-address=10.0.0.21 --status=10080 --log-slow-query=/data/log/slow.log

5. 集群日常运维

5.1 配置变更

# 修改集群配置(会打开编辑器,修改后保存)
tiup cluster edit-config <cluster-name>

# 变更必须 reload 才生效
tiup cluster reload <cluster-name>
tiup cluster reload <cluster-name> -R tidb
tiup cluster reload <cluster-name> -N 10.0.0.21:4000

# 预览将要执行的操作
tiup cluster edit-config <cluster-name> --skip-reload

edit-config 改的是 TiUP 的元数据(~/.tiup/storage/cluster/clusters/<name>/meta.yaml),reload 才会真正下发到节点配置文件并重启进程。直接改节点上的 tikv.toml 会在下次 reload 时被覆盖。

5.2 在线参数调整(优先级高于配置文件)

-- 查看当前值
SHOW GLOBAL VARIABLES LIKE 'tidb_mem_quota_query';

-- 会话级
SET SESSION tidb_distsql_scan_concurrency = 15;

-- 全局级(只影响新会话,或按需立即生效)
SET GLOBAL tidb_mem_quota_query = 8589934592;

-- 查看 TiDB 配置项
SHOW CONFIG WHERE type = 'tidb';
SHOW CONFIG WHERE name LIKE '%mem%';

-- 在线改配置(等价于 SET GLOBAL,但支持更多非变量型配置)
SET CONFIG tidb tikv-client.grpc-keepalive-time = '10s';
-- 集群维度配置查询(一次看所有节点)
SELECT TYPE, INSTANCE, NAME, VALUE FROM information_schema.cluster_config
WHERE NAME IN ('mem-quota-query','storage.block-cache.capacity','log.slow-threshold');

5.3 节点级运维命令

# 在指定节点执行系统命令(批量)
tiup cluster exec <cluster-name> --command "df -h /tidb-data"

# 查看集群审计日志(谁在什么时候执行了 tiup 操作)
tiup cluster audit
tiup cluster audit <audit-id>

# 给集群打补丁(单点热修复,不改版本号)
tiup cluster patch <cluster-name> ./hotfix.tar.gz -R tidb

5.4 时钟与网络检查

# TiDB 依赖全局单调时钟,PD 分配的 TSO 要求各节点时钟偏差可控
for h in 10.0.0.11 10.0.0.12 10.0.0.13 10.0.0.21 10.0.0.31; do
  echo -n "$h: "; ssh $h "date +%s%N"; 
done

# 检查 PD 集群健康
curl http://10.0.0.11:2379/pd/api/v1/health
curl http://10.0.0.11:2379/pd/api/v1/members

6. 连接与账号权限

6.1 客户端连接

# 标准 MySQL 客户端即可
mysql -h 10.0.0.21 -P 4000 -u root -p

# 执行单条语句
mysql -h 10.0.0.21 -P 4000 -u app -p db1 -e "SELECT COUNT(*) FROM t1"

# 带字符集和超时
mysql --default-character-set=utf8mb4 --connect-timeout=10 \
      -h 10.0.0.21 -P 4000 -u app -p

# TiUP 自带客户端
tiup client

连接串必须带 parseTime=true&loc=Local(Go)、characterEncoding=utf8&serverTimezone=Asia/Shanghai(Java),否则时间字段会偏移。这是从 MySQL 切换过来最常见的「数据没错但时间不对」。

6.2 账号管理

-- 创建账号(TiDB 支持 MySQL 语法,但密码插件只支持 mysql_native_password / caching_sha2_password)
CREATE USER 'app'@'10.0.0.%' IDENTIFIED BY 'Strong#Passw0rd';

-- 授权
GRANT SELECT, INSERT, UPDATE, DELETE ON db1.* TO 'app'@'10.0.0.%';
GRANT ALL PRIVILEGES ON db1.* TO 'dba'@'10.0.0.%';

-- 只读账号(报表场景)
CREATE USER 'ro'@'%' IDENTIFIED BY 'Read0nly#2026';
GRANT SELECT ON db1.* TO 'ro'@'%';

FLUSH PRIVILEGES;

-- 查看
SELECT User, Host, plugin FROM mysql.user;
SHOW GRANTS FOR 'app'@'10.0.0.%';

-- 改密
ALTER USER 'app'@'10.0.0.%' IDENTIFIED BY 'New#Passw0rd';

-- 锁定 / 解锁
ALTER USER 'app'@'10.0.0.%' ACCOUNT LOCK;
ALTER USER 'app'@'10.0.0.%' ACCOUNT UNLOCK;

-- 删除
DROP USER 'app'@'10.0.0.%';

坑:TiDB 的权限检查不区分库名大小写在部分版本上表现与 MySQL 不同;且 GRANT 后无需 FLUSH PRIVILEGES(TiDB 无权限缓存),但为了脚本兼容保留也无害。

6.3 会话与连接管理

-- 当前连接
SHOW PROCESSLIST;

-- 按用户/来源汇总
SELECT USER, HOST, COMMAND, COUNT(*) cnt
FROM information_schema.PROCESSLIST
GROUP BY USER, HOST, COMMAND ORDER BY cnt DESC;

-- 长事务/慢查询(超过 60 秒的活跃会话)
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, LEFT(INFO,120) info
FROM information_schema.PROCESSLIST
WHERE COMMAND <> 'Sleep' AND TIME > 60 ORDER BY TIME DESC;

-- 杀会话(TiDB 支持全局 KILL,需开启 enable-global-kill,默认开启)
KILL 12345;
KILL CONNECTION 12345;

-- 按条件批量杀(生成语句后人工确认再执行)
SELECT CONCAT('KILL ', ID, ';')
FROM information_schema.PROCESSLIST
WHERE USER = 'report' AND TIME > 600;
-- 最大连接数
SHOW GLOBAL VARIABLES LIKE 'max_connections';
SET GLOBAL max_connections = 3000;

-- 当前连接数水位
SELECT INSTANCE, COUNT(*) FROM information_schema.CLUSTER_PROCESSLIST GROUP BY INSTANCE;

7. 关键参数与配置

7.1 TiDB 层

参数 默认 建议 说明
max_connections 0(不限) 按业务设 2000~5000 0 并不代表安全,连接打满会 OOM
tidb_mem_quota_query 1 GB 4~8 GB 单条 SQL 内存上限,超限按 oom-action 处理
tidb_mem_oom_action CANCEL CANCEL 绝不要设 LOG,会直接 OOM 掉进程
tidb_server_memory_limit 0 物理内存 80% 全局内存水位,触发后会主动 kill 大查询
tidb_distsql_scan_concurrency 15 15~30 扫描并发,过高会打爆 TiKV
tidb_index_lookup_concurrency 4 4~10 回表并发
tidb_executor_concurrency 5 5 算子并发(v6.5+ 统一参数)
tidb_hash_join_concurrency 5 5~10 HashJoin 并发
tidb_txn_mode pessimistic pessimistic 悲观锁,业务无冲突时可试 optimistic
txn-total-size-limit 100 MiB 1~10 GiB 单事务总大小,超限报 txn too large
txn-entry-size-limit 6 MiB 6~120 MiB 单行大小上限
tidb_gc_life_time 10m 30m~24h 太长会导致历史版本堆积、磁盘暴涨
tidb_enable_async_commit ON ON 异步提交,降低延迟
tidb_enable_1pc ON ON 单分区事务一阶段提交
tidb_isolation_read_engines tikv tikv,tiflash 开启 TiFlash 后需显式加
tidb_allow_batch_cp ON ON 批量 Coprocessor 请求
-- 一次看清关键参数
SELECT VARIABLE_NAME, VARIABLE_VALUE
FROM information_schema.VARIABLES
WHERE VARIABLE_NAME IN (
  'max_connections','tidb_mem_quota_query','tidb_mem_oom_action',
  'tidb_server_memory_limit','tidb_gc_life_time','tidb_txn_mode',
  'txn-total-size-limit','tidb_isolation_read_engines','tidb_distsql_scan_concurrency'
);

7.2 TiKV 层

# tikv.toml 关键项
[storage.block-cache]
capacity = "16GB"          # 建议物理内存 45%,剩余留给 page cache

[readpool.unified]
min-thread-count = 1
max-thread-count = 0       # 0 = CPU 核数

[server]
grpc-concurrency = 8       # 建议 CPU 核数的 1/4~1/2
grpc-raft-conn-num = 1

[raftstore]
apply-pool-size = 4
store-pool-size = 4
hibernate-regions = true   # 空闲 Region 休眠,降低心跳压力

[rocksdb]
max-background-jobs = 8
max-open-files = 1024

[coprocessor]
region-max-size = "144MiB"
region-split-size = "96MiB"
参数 建议值 说明
storage.block-cache.capacity 物理内存 45% 太小会频繁读盘,太大会 OOM
raftstore.hibernate-regions true 大规模集群必开,显著降心跳
server.grpc-concurrency CPU 核数 / 4 过高反而上下文切换
rocksdb.max-background-jobs 8~16 影响 compaction 能力

7.3 PD 层

# 查看调度相关配置
tiup ctl:v8.5.3 pd -u http://10.0.0.11:2379 config show

# 副本数(全局)
tiup ctl:v8.5.3 pd -u http://10.0.0.11:2379 config set max-replicas 3

# 单个 Region 副本数(细粒度,生产上更常用 Placement Rule)
tiup ctl:v8.5.3 pd -u http://10.0.0.11:2379 config set max-replicas 5

# 调度速度限制(扩容/故障恢复时避免打满带宽)
tiup ctl:v8.5.3 pd -u http://10.0.0.11:2379 config set leader-schedule-limit 4
tiup ctl:v8.5.3 pd -u http://10.0.0.11:2379 config set region-schedule-limit 2048
tiup ctl:v8.5.3 pd -u http://10.0.0.11:2379 config set replica-schedule-limit 64

8. 库表对象管理与 TiDB 限制

8.1 库表操作

CREATE DATABASE app_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

-- 建表(TiDB 建议显式指定主键,避免隐藏的 _tidb_rowid)
CREATE TABLE app_db.orders (
  id          BIGINT NOT NULL AUTO_INCREMENT,
  order_no    VARCHAR(32) NOT NULL,
  user_id     BIGINT NOT NULL,
  amount      DECIMAL(18,2) NOT NULL,
  status      TINYINT NOT NULL DEFAULT 0,
  created_at  DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  UNIQUE KEY uk_order_no (order_no),
  KEY idx_user_created (user_id, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

-- 高并发写入且无自增语义时,用打散主键避免写热点
CREATE TABLE app_db.events (
  id      BIGINT PRIMARY KEY AUTO_RANDOM,
  payload JSON
);

-- 加索引(v5.0+ 支持在线加索引,不锁表)
ALTER TABLE app_db.orders ADD INDEX idx_status_created (status, created_at);

-- 加列(v6.2+ 支持并行 DDL,但仍建议在低峰执行)
ALTER TABLE app_db.orders ADD COLUMN remark VARCHAR(255) NULL;

-- 查看表结构
SHOW CREATE TABLE app_db.orders;
DESC app_db.orders;

-- 查看表的 Region 分布(判断热点)
SHOW TABLE app_db.orders REGIONS;
SHOW TABLE app_db.orders INDEX idx_user_created REGIONS;

8.2 DDL 任务管理

TiDB 的 DDL 是异步作业,ALTER 返回成功不代表已完成(尤其是加索引、改类型)。

-- 查看正在运行和历史 DDL
ADMIN SHOW DDL;
ADMIN SHOW DDL JOBS;
ADMIN SHOW DDL JOBS 20;          -- 最近 20 条

-- 取消 DDL(只能取消未完成的)
ADMIN CANCEL DDL JOBS 123, 124;

-- 查看某张表的历史 DDL
ADMIN SHOW DDL JOB QUERIES LIMIT 10;

坑:ADD INDEX 在 TiDB 上是「加索引 + 回填数据」两阶段,大表可能跑几小时。期间 ADMIN SHOW DDL JOBS 的 ROW_COUNT 会持续增长,不要误以为卡住而 cancel,cancel 后重来会更慢。

8.3 统计信息

-- 手动收集(大表建议指定采样率,避免全表扫描)
ANALYZE TABLE app_db.orders;
ANALYZE TABLE app_db.orders WITH 0.1 SAMPLERATE;

-- 只收集索引
ANALYZE TABLE app_db.orders INDEX idx_user_created;

-- 查看统计信息状态
SHOW STATS_META WHERE db_name='app_db' AND table_name='orders';
SHOW STATS_HEALTHY WHERE db_name='app_db' AND table_name='orders';

-- 健康度低于 60 会触发自动收集
SELECT * FROM mysql.stats_meta WHERE table_id = (
  SELECT tidb_table_id FROM information_schema.tables
  WHERE table_schema='app_db' AND table_name='orders'
);
-- 自动收集窗口与并发
SHOW VARIABLES LIKE 'tidb_auto_analyze_%';
SET GLOBAL tidb_auto_analyze_start_time = '01:00 +0000';
SET GLOBAL tidb_auto_analyze_end_time   = '06:00 +0000';
SET GLOBAL tidb_auto_analyze_concurrency = 4;

8.4 执行计划绑定(应急止血)

-- 把当前执行计划固定下来
CREATE GLOBAL BINDING FOR
  SELECT * FROM app_db.orders WHERE user_id = ? AND status = ?
USING
  SELECT * FROM app_db.orders USE INDEX (idx_user_created) WHERE user_id = ? AND status = ?;

SHOW BINDINGS;
DROP BINDING FOR SELECT * FROM app_db.orders WHERE user_id = ? AND status = ?;

计划跑偏导致慢查询时,先建 binding 止血,再慢慢查统计信息。这是 TiDB 上最有效的应急手段,比改 SQL 快得多。


9. 备份与恢复(BR)

9.1 方案选型

方案 类型 速度 一致性 适用
BR(物理) 全量/增量 极快(TB 级小时级) 强一致 生产首选
BR log backup 日志备份 持续 强一致 PITR,必须配全量
Dumpling 逻辑导出 慢 快照一致 跨版本迁移、小库
mysqldump 逻辑导出 很慢 一般 仅小表、临时用
TiDB Lightning 导入 快 — 初始化/迁移导入

9.2 全量备份

# 备份到本地盘(所有 TiKV 节点都要有该路径)
tiup br:v8.5.3 backup full \
  --pd "10.0.0.11:2379" \
  --storage "local:///data/backup/full-20261105" \
  --ratelimit 128 \
  --concurrency 16 \
  --checksum=true

# 备份到 S3 / MinIO
tiup br:v8.5.3 backup full \
  --pd "10.0.0.11:2379" \
  --storage "s3://tidb-backup/full-20261105?endpoint=http://minio:9000&access-key=xxx&secret-key=xxx" \
  --ratelimit 256

# 只备份某个库
tiup br:v8.5.3 backup db --db app_db --pd "10.0.0.11:2379" --storage "local:///data/backup/app_db"

# 只备份某张表(可多表,逗号分隔)
tiup br:v8.5.3 backup table --db app_db --table orders,order_items \
  --pd "10.0.0.11:2379" --storage "local:///data/backup/orders"

# 增量备份(基于上次备份的 ts)
tiup br:v8.5.3 backup full \
  --pd "10.0.0.11:2379" \
  --storage "local:///data/backup/inc-20261106" \
  --lastbackupts 454684561591582721

关键参数:

参数 说明
--ratelimit 单 TiKV 限速 MiB/s,生产建议 128~256,避免打满带宽
--concurrency 备份并发度,默认 4,大集群可到 16~32
--checksum 是否校验,默认 true,生产必须开
--lastbackupts 增量备份起点,取自上次备份输出的 BackupTS
--gcttl 备份期间临时延长 GC(秒),防止备份被 GC 打断
--filter 过滤库表,如 --filter '*.*' --filter '!mysql.*'

备份必须留全所有 TiKV 节点的日志:BR 会在每个 TiKV 的 data_dir 下写临时 SST 文件,--storage 指向的路径也要每个节点可写(local:// 场景)。用 NFS 挂同一个路径最省事,但要评估带宽。

9.3 恢复

# 恢复全量
tiup br:v8.5.3 restore full \
  --pd "10.0.0.11:2379" \
  --storage "local:///data/backup/full-20261105" \
  --ratelimit 128 --concurrency 16

# 恢复单库(表名带库名前缀时必须用 --db 或 --filter)
tiup br:v8.5.3 restore db --db app_db --pd "10.0.0.11:2379" --storage "local:///data/backup/full-20261105"

# 恢复单表到另一个库(改库名)
tiup br:v8.5.3 restore table \
  --db app_db --table orders \
  --pd "10.0.0.11:2379" \
  --storage "local:///data/backup/full-20261105" \
  --rewrite-rules 'app_db.orders -> app_db.orders_bak'

# 恢复到指定时间点(需先有日志备份,见第 10 章)
tiup br:v8.5.3 restore point \
  --pd "10.0.0.11:2379" \
  --full-backup-storage "local:///data/backup/full-20261105" \
  --restored-ts "2026-11-05 09:30:00 +0800"
-- 恢复前必须确认:目标库表已存在会冲突
-- 建议先改名或建独立库
CREATE DATABASE app_db_restore;

-- 恢复后校验行数
SELECT COUNT(*) FROM app_db_restore.orders;

9.4 备份策略建议

对象 频率 保留 说明
全量 BR 每天 1 次(低峰) 7 天 落对象存储,异地再同步一份
增量 BR 每 6 小时 3 天 基于上次 BackupTS
日志备份 持续 7 天 支撑 PITR,br log truncate 控制保留
逻辑导出 每周 1 次 30 天 防「物理备份全坏」的兜底,也便于跨版本迁移

备份不等于可恢复。每月至少做一次真实恢复演练到独立集群,记录 RTO。只验证备份文件存在是自欺欺人。


10. 日志备份与 PITR

10.1 启动日志备份

# 启动(task-name 是任务标识,全局唯一)
tiup br:v8.5.3 log start \
  --task-name=pitr-app \
  --pd "10.0.0.11:2379" \
  --storage "s3://tidb-backup/log?endpoint=http://minio:9000&access-key=xxx&secret-key=xxx" \
  --concurrency=8

# 查看状态(重点看 checkpoint 是否持续推进)
tiup br:v8.5.3 log status --pd "10.0.0.11:2379"

# 暂停 / 恢复
tiup br:v8.5.3 log pause  --task-name=pitr-app --pd "10.0.0.11:2379"
tiup br:v8.5.3 log resume --task-name=pitr-app --pd "10.0.0.11:2379"

# 清理过期日志(保留最近 7 天)
tiup br:v8.5.3 log truncate --task-name=pitr-app --pd "10.0.0.11:2379" --upto 168h

10.2 PITR 完整流程

# 1) 确认可恢复的时间窗口
tiup br:v8.5.3 log status --pd "10.0.0.11:2379"
# 输出中的 "checkpoint" 就是当前可恢复到的最晚时间

# 2) 找一个早于故障时间的全量备份

# 3) 恢复全量
tiup br:v8.5.3 restore full --pd "10.0.0.11:2379" \
  --storage "local:///data/backup/full-20261105"

# 4) 回放到故障前一刻
tiup br:v8.5.3 restore point --pd "10.0.0.11:2379" \
  --full-backup-storage "local:///data/backup/full-20261105" \
  --restored-ts "2026-11-05 09:29:00 +0800"

# 5) 校验
mysql -h 10.0.0.21 -P 4000 -u root -p -e "SELECT COUNT(*) FROM app_db.orders"

三条硬约束:

  1. PITR 要求日志备份的起点早于全量备份的结束时间,否则中间有断档,restore point 会直接报错。
  2. tidb_gc_life_time 必须大于「全量备份间隔 + 日志备份延迟」,否则 GC 会删掉还在用的历史版本。
  3. 恢复期间目标集群不能有写入,建议先停业务或恢复到独立集群再切流量。

11. 数据导入导出(Dumpling / Lightning)

11.1 Dumpling 导出

# 导出整库
tiup dumpling:v8.5.3 \
  -h 10.0.0.21 -P 4000 -u root -p 'xxx' \
  -T app_db \
  -F 256MiB \
  -t 16 \
  -o /data/dump/app_db

# 导出多表 + 只导结构
tiup dumpling:v8.5.3 -h 10.0.0.21 -P 4000 -u root -p 'xxx' \
  -T app_db.orders -T app_db.users \
  --no-data -o /data/dump/schema

# 导出为 SQL 文件(默认是 csv)
tiup dumpling:v8.5.3 -h 10.0.0.21 -P 4000 -u root -p 'xxx' \
  -T app_db --filetype sql -o /data/dump/sql

# 带 WHERE 条件导出(v5.0+)
tiup dumpling:v8.5.3 -h 10.0.0.21 -P 4000 -u root -p 'xxx' \
  -T app_db.orders --where "created_at >= '2026-01-01'"
参数 说明
-F / --filesize 单文件大小,建议 128~256 MiB
-t / --threads 并发线程数,建议为 TiDB 节点数 × 4
--consistency snapshot(默认,需 BR 或 TiDB 支持)/ none
--snapshot 指定 TSO,保证一致性快照
-W / --where 过滤条件

11.2 Lightning 导入

# tidb-lightning.toml
[tidb]
host = "10.0.0.21"
port = 4000
user = "root"
password = "xxx"
status-port = 10080
pd-addr = "10.0.0.11:2379"

[lightning]
level = "info"
file = "/data/log/lightning.log"
region-concurrency = 16

[tikv-importer]
backend = "local"          # local 最快,但导入期间集群不可写;tidb 模式可并行
sorted-kv-dir = "/data/lightning-sst"
disk-quota = "200GB"

[mydumper]
data-source-dir = "/data/dump"
filter = ['*.*', '!mysql.*', '!sys.*']

[checkpoint]
enable = true
driver = "file"
dsn = "/data/lightning-checkpoint"
# 启动导入
tiup tidb-lightning:v8.5.3 -config tidb-lightning.toml

# 查看进度(另开终端)
curl http://10.0.0.51:8289/progress

# 断点续跑:直接重跑同一配置,checkpoint 会跳过已完成表

backend = "local" 会锁表:导入期间目标表不可读写。在线导入用 backend = "tidb"(速度慢 30%~50%,但业务可继续)。这是选型时最容易被忽略的一点。


12. 数据同步(TiCDC / DM)

12.1 TiCDC 变更捕获

# 查看 CDC 节点
tiup ctl:v8.5.3 cdc capture list --server=http://10.0.0.51:8300

# 创建同步任务
tiup ctl:v8.5.3 cdc cli changefeed create \
  --server=http://10.0.0.51:8300 \
  --sink-uri="mysql://root:xxx@10.0.0.61:3306/?time-zone=Asia/Shanghai" \
  --changefeed-id="app-to-mysql" \
  --config=cdc.toml

# 查看任务
tiup ctl:v8.5.3 cdc cli changefeed list --server=http://10.0.0.51:8300
tiup ctl:v8.5.3 cdc cli changefeed query --server=http://10.0.0.51:8300 --changefeed-id=app-to-mysql

# 暂停 / 恢复 / 删除
tiup ctl:v8.5.3 cdc cli changefeed pause  --server=http://10.0.0.51:8300 -c app-to-mysql
tiup ctl:v8.5.3 cdc cli changefeed resume --server=http://10.0.0.51:8300 -c app-to-mysql
tiup ctl:v8.5.3 cdc cli changefeed remove --server=http://10.0.0.51:8300 -c app-to-mysql
# cdc.toml 常用配置
[filter]
rules = ['app_db.*', '!app_db.log_*']

[mounter]
worker-num = 16

[sink]
dispatchers = [
  { matcher = ['app_db.orders'], dispatcher = 'hash', index = 'id' }
]
-- 同步延迟(在 TiCDC 节点看)
-- lag 持续增长说明下游写入跟不上或有大事务

TiCDC 的延迟排查顺序:changefeed query 看 checkpoint-ts 与当前 TSO 的差值 → 看 CDC 节点 CPU/内存 → 看下游 MySQL 是否有慢 SQL 或锁等待 → 看是否有超大事务(单个事务超过 max-txn-size 会被拆,但拆不满仍会拖慢)。

12.2 DM 从 MySQL 迁入 TiDB

# dm-task.yaml
name: mysql-to-tidb
task-mode: all
target-database:
  host: "10.0.0.21"
  port: 4000
  user: "root"
  password: "xxx"
mysql-instances:
  - source-id: "mysql-01"
    block-allow-list: "biz"
    mydumper-config-name: "global"
block-allow-list:
  biz:
    do-dbs: ["app_db"]
mydumpers:
  global:
    threads: 16
    chunk-filesize: 256
tiup dmctl --master-addr 10.0.0.51:8261 start-task dm-task.yaml
tiup dmctl --master-addr 10.0.0.51:8261 query-status
tiup dmctl --master-addr 10.0.0.51:8261 pause-task mysql-to-tidb
tiup dmctl --master-addr 10.0.0.51:8261 resume-task mysql-to-tidb

13. 高可用与 PD 调度

13.1 Store 状态管理

PD="http://10.0.0.11:2379"

# 查看所有 Store
tiup ctl:v8.5.3 pd -u $PD store
tiup ctl:v8.5.3 pd -u $PD store 1        # 单个 Store 详情

# Store 状态:Up(正常)/ Offline(下线中)/ Tombstone(已下线)
# 下线 TiKV(安全迁移 Region)
tiup ctl:v8.5.3 pd -u $PD store delete 4

# 强制删除(节点已物理损坏,不再回来)
tiup ctl:v8.5.3 pd -u $PD store delete 4 --force

store delete 会触发 Region 迁移,必须等该 Store 变成 Tombstone 再关机。如果直接 rm 数据目录,会丢副本导致 Region 不可用。

13.2 Region 与热点

# 查看 Region 总数与健康度
tiup ctl:v8.5.3 pd -u $PD region
tiup ctl:v8.5.3 pd -u $PD region check miss-peer
tiup ctl:v8.5.3 pd -u $PD region check extra-peer
tiup ctl:v8.5.3 pd -u $PD region check down-peer
tiup ctl:v8.5.3 pd -u $PD region check pending-peer

# 热点(读写)
tiup ctl:v8.5.3 pd -u $PD hot read
tiup ctl:v8.5.3 pd -u $PD hot write

# 查看某张表的 Region 分布
# SQL 侧更快:SHOW TABLE app_db.orders REGIONS;
-- Region 分布统计(按 Store)
SELECT STORE_ID, COUNT(*) region_cnt, SUM(APPROXIMATE_SIZE)/1024 size_mb
FROM information_schema.TIKV_REGION_STATUS
GROUP BY STORE_ID ORDER BY region_cnt DESC;

-- 某张表落在哪些 Store
SELECT STORE_ID, COUNT(*) FROM information_schema.TIKV_REGION_STATUS
WHERE TABLE_NAME = 'orders' GROUP BY STORE_ID;

13.3 Placement Rules(跨机房容灾)

-- 创建放置策略:2 副本在同城 A,1 副本在同城 B
CREATE PLACEMENT POLICY `east-west-2-1`
  PRIMARY_REGION="east"
  REGIONS="east,east,west"
  FOLLOWERS=2
  SCHEDULE="EVEN";

-- 应用到表
ALTER TABLE app_db.orders PLACEMENT POLICY=`east-west-2-1`;

-- 查看
SHOW PLACEMENT;
SHOW PLACEMENT FOR TABLE app_db.orders;
SHOW PLACEMENT LABELS;

-- 取消策略(回到全局默认)
ALTER TABLE app_db.orders PLACEMENT POLICY=DEFAULT;

跨城场景下 TiDB 的写入延迟由最远的那个副本决定(Raft 多数派提交)。三副本跨城 = 每次提交至少要等跨城 RTT,这是物理限制,不能靠调参解决。正确做法是同城 2 副本 + 异地异步 DR。

13.4 常用调度器

# 驱逐某个 Store 上的 Leader(维护前把流量挪走,不迁移数据)
tiup ctl:v8.5.3 pd -u $PD scheduler add evict-leader-scheduler 4
tiup ctl:v8.5.3 pd -u $PD scheduler remove evict-leader-scheduler-4

# 查看当前调度器
tiup ctl:v8.5.3 pd -u $PD scheduler show

# 手动下线(比 store delete 更平滑)
tiup ctl:v8.5.3 pd -u $PD store delete 4

# 冻结调度(故障排查时临时止血,事后必须恢复)
tiup ctl:v8.5.3 pd -u $PD config set leader-schedule-limit 0
tiup ctl:v8.5.3 pd -u $PD config set region-schedule-limit 0
tiup ctl:v8.5.3 pd -u $PD config set replica-schedule-limit 0

14. 日志与监控体系

14.1 日志位置

组件 默认路径 说明
TiDB /tidb-deploy/tidb-4000/log/tidb.log 慢查询在 tidb-slow.log
TiKV /tidb-deploy/tikv-20160/log/tikv.log 量大,注意轮转
PD /tidb-deploy/pd-2379/log/pd.log —
TiFlash /tidb-deploy/tiflash-9000/log/tiflash.log —
TiCDC /tidb-deploy/cdc-8300/log/cdc.log —
# 实时跟踪
tail -f /tidb-deploy/tidb-4000/log/tidb.log

# 找错误
grep -iE '\bERROR\b|\bFATAL\b|\bpanic\b' /tidb-deploy/tikv-20160/log/tikv.log | tail -100

# 按时间窗过滤
awk '/2026\/11\/05 09:2[0-9]/,/2026\/11\/05 09:3[0-9]/' tidb.log | grep -i error

# 统计错误类型 Top10
grep -oE '\[error\][^\]]*' tikv.log | sort | uniq -c | sort -rn | head -10

# 清理 7 天前的日志
find /tidb-deploy -name "*.log.*" -mtime +7 -delete

14.2 慢查询分析

-- 慢查询表(v6.1+ 按时间分表,直接查即可)
SELECT * FROM information_schema.slow_query
WHERE time > '2026-11-05 09:00:00'
ORDER BY query_time DESC LIMIT 20;

-- 关键字段
SELECT
  time,
  query_time,
  parse_time,
  compile_time,
  process_keys,
  total_keys,
  ROUND(memory_max/1024/1024, 2) AS mem_mb,
  LEFT(query, 100) AS sql_digest
FROM information_schema.slow_query
WHERE time > DATE_SUB(NOW(), INTERVAL 1 HOUR)
ORDER BY query_time DESC LIMIT 20;

-- 按 SQL 指纹聚合(找出最耗时的模板)
SELECT digest, COUNT(*) cnt, AVG(query_time) avg_s, MAX(query_time) max_s,
       SUM(process_keys) keys, MIN(query) sample
FROM information_schema.slow_query
WHERE time > DATE_SUB(NOW(), INTERVAL 1 HOUR)
GROUP BY digest ORDER BY avg_s DESC LIMIT 20;
-- 语句汇总(v6.1+,比 slow_query 更适合做 TOP N)
SELECT digest_text, exec_count,
       ROUND(avg_latency/1e9, 4) avg_sec,
       ROUND(max_latency/1e9, 2) max_sec,
       ROUND(sum_latency/1e9, 2) total_sec,
       avg_processed_keys,
       sum_rows_examined
FROM information_schema.statement_summary
ORDER BY total_sec DESC LIMIT 20;

14.3 Dashboard 与监控

# TiDB Dashboard(内置于 PD,v4.0+)
# 地址:http://<pd-ip>:2379/dashboard
# 默认账号:root / <root 密码>(v6.5+ 支持 SSO)

# Grafana
# 地址:http://<monitoring-ip>:3000
# 默认账号:admin / admin

# Prometheus
# 地址:http://<monitoring-ip>:9090

Dashboard 里最常用的三个页面:

页面 用途
慢查询(Slow Query) 按耗时排序,能直接看到执行计划与各阶段耗时
SQL 语句分析 按执行次数/总耗时聚合,定位 TOP SQL
集群诊断(Diagnose) 一键生成诊断报告,适合故障时快速取证
# 命令行也能拉诊断报告
tiup diag collect <cluster-name> --from "2026-11-05 09:00" --to "2026-11-05 10:00"

# 需要先部署 diag 组件
tiup install diag

15. 性能诊断

15.1 慢查询四步法

① 定位:information_schema.slow_query / statement_summary
② 看计划:EXPLAIN ANALYZE,重点看 operator info 里的 rows 与 actRows 偏差
③ 判因:走错索引?Region 热点?TiKV 侧 Coprocessor 慢?网络回表多?
④ 处置:加索引 / 建 binding / 调并发 / 拆 SQL
-- 完整执行计划(v6.1+ 输出更直观)
EXPLAIN ANALYZE
SELECT o.id, o.amount, u.name
FROM app_db.orders o JOIN app_db.users u ON o.user_id = u.id
WHERE o.created_at >= '2026-11-01' AND o.status = 1;

-- 简洁模式(大计划时便于阅读)
EXPLAIN FORMAT='brief' SELECT ...;

-- 只看访问路径
EXPLAIN SELECT * FROM app_db.orders WHERE user_id = 1001;

执行计划危险信号:

现象 含义 处置
TableFullScan + rows 很大 全表扫描 补索引,或改写条件
estRows 与 actRows 相差 > 10 倍 统计信息不准 ANALYZE TABLE,必要时 WITH 0.1 SAMPLERATE
cop_task 数量异常多 Region 过多或跨机读 检查 Region 大小,考虑 MERGE REGION
tikv_task 时间长但 process_keys 小 TiKV 侧 CPU 打满或磁盘慢 看 TiKV Grafana 的 RocksDB 指标
HashJoin 的 build 侧很大 小表没走广播 收集统计信息或 STRAIGHT_JOIN 固定驱动表

15.2 热点诊断

-- 写入热点:某个 Region 的 written_bytes 远高于均值
SELECT REGION_ID, STORE_ID, WRITTEN_BYTES, READ_BYTES
FROM information_schema.TIKV_REGION_STATUS
ORDER BY WRITTEN_BYTES DESC LIMIT 20;

-- 自增主键导致的写热点:确认表是否有 AUTO_INCREMENT 主键
SELECT TABLE_SCHEMA, TABLE_NAME, COLUMN_NAME, EXTRA
FROM information_schema.COLUMNS
WHERE EXTRA LIKE '%auto_increment%' AND TABLE_SCHEMA NOT IN ('mysql','sys');
# PD 侧热点
tiup ctl:v8.5.3 pd -u http://10.0.0.11:2379 hot write
tiup ctl:v8.5.3 pd -u http://10.0.0.11:2379 hot read

# 打散热点 Region(临时手段)
tiup ctl:v8.5.3 pd -u http://10.0.0.11:2379 operator add split-region <region-id> --policy=approximate

写热点根因 90% 是自增主键。TiDB 按主键范围切 Region,自增 ID 会让所有写入落到最后一个 Region。解决:用 AUTO_RANDOM、业务侧打散(如 id = snowflake % N 做前缀)、或 SHARD_ROW_ID_BITS。

-- 已有表改造:把自增主键换成 AUTO_RANDOM 需要重建表
CREATE TABLE orders_new LIKE orders;
ALTER TABLE orders_new MODIFY id BIGINT AUTO_RANDOM;
INSERT INTO orders_new SELECT * FROM orders;
RENAME TABLE orders TO orders_old, orders_new TO orders;

-- 非聚簇表打散 RowID
ALTER TABLE log_table SHARD_ROW_ID_BITS = 4;

15.3 TiKV 侧诊断

# 查看 TiKV 状态
curl http://10.0.0.31:20180/metrics | grep -E 'tikv_raftstore_region_count|tikv_engine_size'

# Region 详情(tikv-ctl 需要停实例或用 --host 远程)
tikv-ctl --host 10.0.0.31:20160 region --key "hex_key"

# 手动 compaction(仅应急,会吃满 IO)
tikv-ctl --host 10.0.0.31:20160 compact -d kv --db /tidb-data/tikv-20160/db

# 查看 Raft 状态机
tikv-ctl --host 10.0.0.31:20160 raft region -r <region-id>
-- TiKV 线程与存储状态
SELECT * FROM information_schema.TIKV_STORE_STATUS;
SELECT STORE_ID, ADDRESS, STORE_STATE_NAME, CAPACITY, AVAILABLE
FROM information_schema.TIKV_STORE_STATUS;

15.4 内存与 OOM

-- 当前内存使用
SELECT INSTANCE, ROUND(SUM(MEMORY_USAGE)/1024/1024, 2) mem_mb
FROM information_schema.CLUSTER_MEMORY_USAGE GROUP BY INSTANCE;

-- TiDB 内存表(v6.5+)
SELECT * FROM information_schema.MEMORY_USAGE_OPS_HISTORY ORDER BY TIME DESC LIMIT 20;

-- 触发 OOM 的 SQL 会记录在这里
SELECT * FROM information_schema.MEMORY_USAGE_OPS_HISTORY
WHERE MEMORY_USAGE > 1073741824;
-- 三层防护,建议全开
SET GLOBAL tidb_mem_quota_query = 8589934592;        -- 单查询 8 GB
SET GLOBAL tidb_server_memory_limit = '60GB';         -- 全局 60 GB(物理内存 80%)
SET GLOBAL tidb_server_memory_limit_sess_min_size = 134217728;  -- 单会话最小 128 MB

顺序很重要:tidb_server_memory_limit 触发时会 kill 占用最大的会话,所以 tidb_mem_quota_query 必须小于全局上限,否则单条 SQL 就能顶到全局阈值,导致误杀其他会话。


16. GC 与空间管理

16.1 GC 机制

TiDB 用 MVCC 保存多版本,GC 负责清理旧版本。safe_point 之前的数据不可恢复。

-- 查看 GC 状态
SELECT * FROM mysql.tidb WHERE variable_name LIKE 'tikv_gc%';

-- 关键两项
-- tikv_gc_safe_point:可恢复的最早时间点
-- tikv_gc_last_run_time:上次 GC 时间

-- 调整保留时长(默认 10 分钟,生产建议 30 分钟~24 小时)
SET GLOBAL tidb_gc_life_time = '24h';

-- 最长等待时间(大事务/长查询会阻塞 GC)
SET GLOBAL tidb_gc_max_wait_time = '86400';

-- 手动触发 GC(谨慎,会立即回收 safe_point 之前的数据)
SET GLOBAL tidb_gc_enable = OFF;
SET GLOBAL tidb_gc_enable = ON;   -- 重新开启时立即触发一轮

tidb_gc_life_time 的取舍:

  • 设太短(如 10m):备份/PITR 无法覆盖,误删数据救不回来;
  • 设太长(如 7d):历史版本堆积,磁盘可能翻倍,且 GC 单轮耗时变长。 生产建议 24h,配合每天全量备份 + 日志备份即可覆盖绝大多数误操作。

16.2 空间统计

-- 库级容量
SELECT TABLE_SCHEMA,
       ROUND(SUM(DATA_LENGTH)/1024/1024/1024, 2) data_gb,
       ROUND(SUM(INDEX_LENGTH)/1024/1024/1024, 2) index_gb,
       ROUND(SUM(DATA_LENGTH+INDEX_LENGTH)/1024/1024/1024, 2) total_gb,
       COUNT(*) table_cnt
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql','sys','information_schema','performance_schema','METRICS_SCHEMA','INSPECTION_SCHEMA')
GROUP BY TABLE_SCHEMA ORDER BY total_gb DESC;

-- 单表 Top20
SELECT TABLE_SCHEMA, TABLE_NAME,
       TABLE_ROWS,
       ROUND((DATA_LENGTH+INDEX_LENGTH)/1024/1024/1024, 2) total_gb
FROM information_schema.TABLES
WHERE TABLE_SCHEMA NOT IN ('mysql','sys','information_schema','performance_schema')
ORDER BY (DATA_LENGTH+INDEX_LENGTH) DESC LIMIT 20;

-- TiKV 实际磁盘占用(比 information_schema 更准)
SELECT STORE_ID, ADDRESS,
       ROUND(CAPACITY/1024/1024/1024, 2) cap_gb,
       ROUND(AVAILABLE/1024/1024/1024, 2) avail_gb,
       ROUND((CAPACITY-AVAILABLE)/1024/1024/1024, 2) used_gb
FROM information_schema.TIKV_STORE_STATUS;

16.3 回收空间

-- 1) 删数据(分批,避免大事务)
DELETE FROM app_db.logs WHERE created_at < '2026-01-01' LIMIT 10000;
-- 循环执行直到 affected rows = 0

-- 2) 回收表空间(TiDB 的 DROP/TRUNCATE 是异步 GC,不会立刻释放磁盘)
DROP TABLE app_db.logs_old;

-- 3) 确认 GC 已推进(等 safe_point 越过 drop 时间)
SELECT * FROM mysql.tidb WHERE variable_name = 'tikv_gc_safe_point';

-- 4) 看 Region 是否已下线
SELECT COUNT(*) FROM information_schema.TIKV_REGION_STATUS WHERE TABLE_NAME = 'logs_old';
# 5) 手工 compaction(GC 后磁盘不降时用,会吃满 IO,低峰执行)
tikv-ctl --host 10.0.0.31:20160 compact -d kv --db /tidb-data/tikv-20160/db -c write

注意:TiDB 删除数据后磁盘不会立即下降,这是正常的。Region 需要经过 GC → compaction 才会真正释放。经验上,删除 1 TB 数据,磁盘在 24~72 小时内逐步回落。


17. 扩缩容与版本升级

17.1 扩容

# 1) 编辑拓扑,加入新节点
tiup cluster edit-config <cluster-name>

# 2) 扩容(只对新节点生效)
tiup cluster scale-out <cluster-name> scale-out.yaml --user root -p

# 3) 观察 Region 迁移进度
tiup ctl:v8.5.3 pd -u http://10.0.0.11:2379 store
tiup ctl:v8.5.3 pd -u http://10.0.0.11:2379 region check miss-peer
# scale-out.yaml 示例:加一个 TiKV
tikv_servers:
  - host: 10.0.0.34
    port: 20160
    status_port: 20180
    deploy_dir: "/tidb-deploy/tikv-20160"
    data_dir: "/tidb-data/tikv-20160"

17.2 缩容

# 缩容(TiUP 会先调用 PD 下线接口,等 Region 迁移完再停进程)
tiup cluster scale-in <cluster-name> -N 10.0.0.34:20160

# 如果卡住(节点已失联),强制下线
tiup cluster scale-in <cluster-name> -N 10.0.0.34:20160 --force

# 清理已下线节点的残留元数据
tiup cluster prune <cluster-name>

缩容 TiKV 不要图快加 --force。--force 会跳过数据迁移直接删 Store 元数据,如果该 Store 上有单副本 Region,直接丢数据。只在节点物理损坏且确认无单副本时使用。

17.3 版本升级

# 1) 检查当前版本与目标版本
tiup cluster display <cluster-name>
tiup list tidb

# 2) 升级前必做:全量备份
tiup br:v8.5.3 backup full --pd "10.0.0.11:2379" --storage "local:///data/backup/pre-upgrade"

# 3) 升级(TiUP 会滚动重启,逐个节点)
tiup cluster upgrade <cluster-name> v8.5.3 --transfer-timeout 600

# 4) 验证
tiup cluster display <cluster-name>
mysql -h 10.0.0.21 -P 4000 -u root -p -e "SELECT tidb_version()"
参数 说明
--transfer-timeout 等待 Leader 迁移的超时秒数,大集群建议 600~1800
--force 跳过版本兼容检查,生产禁用
--offline 离线升级(不联网检查)

升级路径约束:

v6.5 → v7.5 → v8.1 → v8.5     ✅ 逐级
v6.5 → v8.5                    ⚠️ 官方支持但需先做兼容性评估,且不能跨过废弃特性
v5.4 → v8.5                    ❌ 必须先升到 v6.5

升级前必须确认的三件事:

  1. 废弃特性:如 tidb_enable_clustered_index 在 v5.0 后语义变化,v6.0 起默认开启;
  2. 权限模型:v6.0 起 SYSTEM_VARIABLES_ADMIN 等动态权限生效,老账号可能报错;
  3. 客户端驱动:v7.0 起默认关闭 tidb_enable_prepared_plan_cache 的部分行为,需回归测试。

18. 高频故障速查

18.1 连不上

现象 排查顺序
Can't connect to MySQL server ① tiup cluster display 看 TiDB 是否 Up → ② ss -lntp | grep 4000 → ③ 防火墙/安全组 → ④ max_connections 是否打满
连接被拒但进程在 看 tidb.log 是否有 too many connections;临时 SET GLOBAL max_connections = 5000
连接超时 检查负载均衡(SLB/F5)后端健康检查是否误判;TiDB 的 advertise-address 是否配错
# 快速定位
tiup cluster display <cluster-name> | grep -E 'tidb|tikv|pd'
ss -lntp | grep -E '4000|20160|2379'
curl -s http://10.0.0.21:10080/status | head -20
mysql -h 10.0.0.21 -P 4000 -u root -p -e "SELECT 1"

18.2 写入卡住 / TSO 超时

-- 典型报错:PD server timeout / region unavailable / tso worker is closed
根因 判断方法 处置
PD 失去多数派 curl :2379/pd/api/v1/members 看 health 恢复 PD 节点,必要时 pd-recover
TiKV 磁盘写满 df -h;Grafana TiKV → Store Size 清理数据或扩容
Region 无 Leader region check miss-peer 有大量结果 等副本补全;单副本时用 unsafe-recover
大事务打满 information_schema.slow_query 有超大事务 KILL,拆批处理
# PD 健康检查
curl -s http://10.0.0.11:2379/pd/api/v1/health | python3 -m json.tool

# 强制恢复 PD(所有 PD 都挂了且无法恢复时的最后手段)
tiup pd-recover -endpoints http://10.0.0.11:2379 -cluster-id <id> -alloc-id <id+100000>

pd-recover 是不可逆操作,只能在「PD 数据全丢、TiKV 还在」的场景用。执行前必须确认集群 ID 和已分配 ID,写错会导致 TiKV 全部拒绝接入。

18.3 慢查询突然增多

① 是否有 DDL 在跑?        ADMIN SHOW DDL JOBS
② 统计信息是否过期?        SHOW STATS_HEALTHY
③ 是否新上线 SQL?          statement_summary 按 first_seen 排序
④ TiKV 是否热点?           pd hot read/write
⑤ 是否网络/磁盘抖动?       Grafana TiKV Duration / Disk Latency

18.4 Region 异常

# 各类异常 Region 数量
for t in miss-peer extra-peer down-peer pending-peer offline-peer; do
  echo -n "$t: "; tiup ctl:v8.5.3 pd -u http://10.0.0.11:2379 region check $t | grep -c 'region_id' || true
done
类型 含义 处置
miss-peer 副本数少于设定值 等自动补全;若长期不补,检查是否有 Tombstone Store 未清理
extra-peer 副本数多于设定值 通常是缩容残留,自动清理
down-peer 有副本所在 Store 失联 恢复节点;超过 max-store-down-time(默认 30m)会补副本
pending-peer Raft 日志落后的副本 网络问题或磁盘慢

18.5 TiFlash 不可用

-- 检查 TiFlash 副本状态
SELECT * FROM information_schema.TIFLASH_REPLICA;
SELECT TABLE_SCHEMA, TABLE_NAME, REPLICA_COUNT, AVAILABLE, PROGRESS FROM information_schema.TIFLASH_REPLICA;

-- 加 TiFlash 副本
ALTER TABLE app_db.orders SET TIFLASH REPLICA 1;

-- 删除
ALTER TABLE app_db.orders SET TIFLASH REPLICA 0;

PROGRESS < 1 表示还在同步。同步期间查询 TiFlash 会报 region unavailable,此时应确保 tidb_isolation_read_engines='tikv,tiflash',让优化器自动回退到 TiKV。


19. 危险命令与避坑清单

# 命令/操作 风险 防护
1 tiup cluster destroy 删集群 + 所有数据,不可恢复 执行前 tiup cluster display 核对集群名;生产禁用
2 pd-ctl store delete --force 跳过 Region 迁移,可能丢单副本数据 只在节点物理损坏时用,先 region check
3 DROP DATABASE / TRUNCATE 数据立即不可见,只能靠备份/PITR 开启 tidb_enable_ddl_privilege_check;生产禁止 root 直连
4 UPDATE/DELETE 不带 WHERE 全表修改,事务超限会失败但已改部分生效 sql_safe_updates=ON;先用 SELECT COUNT(*) 验证
5 SET GLOBAL tidb_gc_life_time='10m' safe_point 前移,备份无法覆盖 改动前确认备份策略;改完观察 24h
6 ADMIN CANCEL DDL JOBS 取消大表加索引,回滚可能比等待更慢 看 ROW_COUNT 是否在增长,增长就别 cancel
7 直接 rm -rf TiKV 数据目录 Region 丢副本,可能不可用 先 store delete 等 Tombstone
8 tiup cluster upgrade --force 跳过兼容检查,可能升级后起不来 生产禁用,先在测试集群演练
9 tikv-ctl compact 全库 吃满 IO,业务抖动 低峰执行,限定 -c write 或单 ColumnFamily
10 在 TiKV 数据盘上跑备份 BR 临时文件 + 业务数据争抢 IO --storage 指向独立盘或对象存储

变更前五步检查

  1. 备份:br backup full 成功,记录 BackupTS;
  2. 影响面:这条命令影响几个节点?会不会触发 Region 迁移?
  3. 可回滚:失败后怎么退?配置文件是否已备份(cp tikv.toml tikv.toml.bak)?
  4. 观察窗口:变更后至少观察 15 分钟,看 Grafana 的 Duration、Error、Region 三条曲线;
  5. 留痕:tiup cluster audit 会自动记录,但人工也要写变更单,注明时间、命令、执行人。

20. 附录:一键巡检脚本

#!/usr/bin/env bash
# tidb_health_check.sh —— 一分钟巡检
# 用法:./tidb_health_check.sh <cluster-name> <pd-addr> <tidb-host>
set -uo pipefail

CLUSTER="${1:?用法: $0 <cluster-name> <pd-addr> <tidb-host>}"
PD="${2:?}"
TIDB_HOST="${3:?}"

echo "==================== TiDB 巡检 $(date '+%F %T') ===================="

echo -e "\n--- 1. 集群组件状态 ---"
tiup cluster display "$CLUSTER" 2>/dev/null | sed -n '1,40p'

echo -e "\n--- 2. Store 状态 ---"
tiup ctl:v8.5.3 pd -u "$PD" store 2>/dev/null | grep -E '"state_name"|"address"|"capacity"' | head -30

echo -e "\n--- 3. 异常 Region ---"
for t in miss-peer extra-peer down-peer pending-peer; do
  n=$(tiup ctl:v8.5.3 pd -u "$PD" region check "$t" 2>/dev/null | grep -c '"region_id"' || true)
  printf "  %-14s %s\n" "$t" "$n"
done

echo -e "\n--- 4. 连接数与 QPS ---"
mysql -h "$TIDB_HOST" -P 4000 -u root -p"${TIDB_PASS:-}" -N -e "
SELECT CONCAT('连接数: ', COUNT(*)) FROM information_schema.PROCESSLIST;
SELECT CONCAT('活跃会话: ', COUNT(*)) FROM information_schema.PROCESSLIST WHERE COMMAND <> 'Sleep';
" 2>/dev/null

echo -e "\n--- 5. 慢查询 TOP5(近 1 小时)---"
mysql -h "$TIDB_HOST" -P 4000 -u root -p"${TIDB_PASS:-}" -e "
SELECT ROUND(query_time,3) qtime, ROUND(parse_time,4) ptime,
       ROUND(compile_time,4) ctime, LEFT(query,80) sql
FROM information_schema.slow_query
WHERE time > DATE_SUB(NOW(), INTERVAL 1 HOUR)
ORDER BY query_time DESC LIMIT 5;" 2>/dev/null

echo -e "\n--- 6. 统计信息健康度(<60 需关注)---"
mysql -h "$TIDB_HOST" -P 4000 -u root -p"${TIDB_PASS:-}" -e "
SELECT db_name, table_name, healthy
FROM mysql.stats_healthy
WHERE healthy < 60 ORDER BY healthy LIMIT 10;" 2>/dev/null

echo -e "\n--- 7. GC 状态 ---"
mysql -h "$TIDB_HOST" -P 4000 -u root -p"${TIDB_PASS:-}" -e "
SELECT variable_name, variable_value FROM mysql.tidb
WHERE variable_name IN ('tikv_gc_safe_point','tikv_gc_last_run_time','tikv_gc_life_time');" 2>/dev/null

echo -e "\n--- 8. TiKV 磁盘水位 ---"
mysql -h "$TIDB_HOST" -P 4000 -u root -p"${TIDB_PASS:-}" -e "
SELECT STORE_ID, ADDRESS,
       ROUND((CAPACITY-AVAILABLE)/CAPACITY*100,1) AS used_pct
FROM information_schema.TIKV_STORE_STATUS ORDER BY used_pct DESC;" 2>/dev/null

echo -e "\n--- 9. 各组件 ERROR 日志计数(近 200 行)---"
for f in /tidb-deploy/tidb-4000/log/tidb.log \
         /tidb-deploy/tikv-20160/log/tikv.log \
         /tidb-deploy/pd-2379/log/pd.log; do
  [ -f "$f" ] && printf "  %-45s %s\n" "$f" "$(tail -200 "$f" | grep -ciE '\bERROR\b|\bFATAL\b')"
done

echo -e "\n==================== 巡检结束 ===================="

速查表

需求 命令
集群状态 tiup cluster display <name>
改配置 tiup cluster edit-config <name> → tiup cluster reload <name>
全量备份 tiup br backup full --pd <pd> --storage <uri>
恢复到时间点 tiup br restore point --restored-ts "<time>"
看 DDL 进度 ADMIN SHOW DDL JOBS
看慢查询 SELECT * FROM information_schema.slow_query
看执行计划 EXPLAIN ANALYZE <sql>
热点 pd-ctl hot read / hot write
下线 TiKV pd-ctl store delete <id>
扩缩容 tiup cluster scale-out / scale-in
升级 tiup cluster upgrade <name> <version>
诊断包 tiup diag collect <name>

最后一句:TiDB 的运维难点不在单条命令,而在理解它是分布式系统。任何一个「慢」都要先问:是 SQL 层、TiKV 层、PD 层,还是网络?分层定位比背命令有用得多。

TiDB 常用运维指令手册 - 知识库 - 起航社区