TiDB 常用运维指令手册
TiDB 常用运维指令手册
适用版本:TiDB v6.5 / v7.5 / v8.1 / v8.5 LTS(当前推荐 v8.5) 约定:
#root 或 sudo,$普通用户,tidb>MySQL 协议客户端提示符,<xxx>为占位符。 差异标注:正文用> **版本差异**引用块标注跨版本不一致之处。 更新时间:2026-11
目录
- 版本与支持周期
- 架构与组件职责
- 下载与安装
- 集群部署(TiUP)
- 集群日常运维
- 连接与账号权限
- 关键参数与配置
- 库表对象管理与 TiDB 限制
- 备份与恢复(BR)
- 日志备份与 PITR
- 数据导入导出(Dumpling / Lightning)
- 数据同步(TiCDC / DM)
- 高可用与 PD 调度
- 日志与监控体系
- 性能诊断
- GC 与空间管理
- 扩缩容与版本升级
- 高频故障速查
- 危险命令与避坑清单
- 附录:一键巡检脚本
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 升级的路径。这意味着:
- 现在新上生产,选 v8.5 LTS 是唯一稳妥选择;
- 不要把 v8.5 当作「还能用五年不用管」的版本,升级窗口需要按厂商路线图提前排期;
- 招标/立项文档里写版本时,建议写「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"
三条硬约束:
- PITR 要求日志备份的起点早于全量备份的结束时间,否则中间有断档,
restore point会直接报错。tidb_gc_life_time必须大于「全量备份间隔 + 日志备份延迟」,否则 GC 会删掉还在用的历史版本。- 恢复期间目标集群不能有写入,建议先停业务或恢复到独立集群再切流量。
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
升级前必须确认的三件事:
- 废弃特性:如
tidb_enable_clustered_index在 v5.0 后语义变化,v6.0 起默认开启;- 权限模型:v6.0 起
SYSTEM_VARIABLES_ADMIN等动态权限生效,老账号可能报错;- 客户端驱动: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 指向独立盘或对象存储 |
变更前五步检查
- 备份:
br backup full成功,记录BackupTS; - 影响面:这条命令影响几个节点?会不会触发 Region 迁移?
- 可回滚:失败后怎么退?配置文件是否已备份(
cp tikv.toml tikv.toml.bak)? - 观察窗口:变更后至少观察 15 分钟,看 Grafana 的
Duration、Error、Region三条曲线; - 留痕:
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 层,还是网络?分层定位比背命令有用得多。