TiDB 部署不是“下载-解压-启动”三步走那么简单。很多团队第一次接触分布式数据库,最容易踩的坑是把 TiDB 当成单机 MySQL 去规划拓扑,结果上线后延迟抖动、扩缩容失败、甚至数据丢失。真正落地的第一件事,是理解 TiDB 的存算分离架构:TiDB Server 负责 SQL 解析与优化,PD 负责调度与元数据,TiKV 负责行式存储,TiFlash 负责列式分析。这意味着部署时,网络延迟、磁盘 IOPS、CPU 核数、内存容量必须按组件特性分别规划,而不是平均分配资源。
生产环境强烈建议最少 6 台物理机或虚拟机起步:3 台 PD、3 台 TiKV,TiDB Server 无状态可弹性扩展。PD 节点对磁盘吞吐要求不高,但对延迟极其敏感,必须独占 CPU 核心,绝不能与 TiKV 混部在同一块 NVMe 盘上争抢 IO。TiKV 的数据目录必须使用 NVMe SSD,文件系统推荐 ext4 并挂载 nodelalloc 和 noatime 选项。很多人忽略的关键参数是 raftstore.apply-pool-size 和 raftstore.store-pool-size,默认值偏保守,生产环境建议分别调到 4 到 6,能显著提升 Raft 日志应用和写入吞吐。
部署工具 TiUP 虽然简化了操作,但 topology.yaml 的编写质量直接决定集群稳定性。一个常见错误是把所有组件混部在同一批机器上,然后通过端口区分。这在测试环境没问题,生产环境一旦出现磁盘故障或内核崩溃,会导致 PD、TiKV、TiDB Server 同时失联,集群直接不可用。正确的做法是严格物理隔离:PD 用低配虚机或物理机,TiKV 独占高性能存储节点,TiDB Server 可以容器化或混部在应用侧。另外,server.labels 必须提前规划好,比如按机架、机房打标签,这样 PD 才能基于 label 做跨机架、跨 AZ 的副本调度,否则一旦单机架断电,三副本可能全丢在同一故障域。
部署完成后,第一个要跑的诊断命令不是 tiup cluster display,而是 tiup ctl pd store 检查所有 store 状态是否 Up,以及 tiup ctl pd region 检查 Region 分布是否均衡。很多集群上线初期一切正常,一旦数据量突破 100GB,热点 Region 就开始出现。根本原因是建表时没有正确设计主键和分区。TiDB 的主键默认聚簇,如果主键是自增 ID 或单调递增的时间戳,所有写入都会集中到最后一个 Region,形成严重热点。解决方法是使用 SHARD_ROW_ID_BITS 打散隐式主键,或者用 UUID、雪花算法生成随机主键,再或者用 HASH 分区显式打散。
SQL 优化在 TiDB 里和 MySQL 有本质区别。TiDB 的优化器基于统计信息,但默认的 ANALYZE TABLE 采样率在大表上可能严重失真,导致执行计划选错索引。生产环境必须配置 tidb_analyze_version = 2,并定期通过 ANALYZE TABLE t PARTITION p0 按分区增量收集统计信息。另一个高频问题是执行计划缓存失效。很多 DBA 发现同样的 SQL 有时快有时慢,一看慢日志,发现走了全表扫描。这通常是因为 SQL 里带了变量或函数,优化器无法命中 Plan Cache,每次都要重新解析生成执行计划。解决办法是把这类 SQL 改写为绑定执行计划,用 CREATE GLOBAL BINDING 强制指定索引,或者升级到 TiDB 6.5 以上版本开启 Non-Prepared Plan Cache 特性。
TiKV 的性能调优核心在于 Raft 组件的参数匹配硬件能力。raftstore.apply-pool-size 和 raftstore.store-pool-size 已经提过,但更隐蔽的是 storage.scheduler-worker-pool-size 和 server.grpc-concurrency。前者控制写入调度线程数,后者控制 gRPC 并发处理能力。如果 TiKV 节点 CPU 核数超过 32,这两个参数建议调到 8 到 10,否则 CPU 用不满,写入延迟却降不下来。另外,storage.block-cache.capacity 是 RocksDB 的块缓存,默认占总内存 45%,对于读多写少的场景可以调到 60%,但要给操作系统 Page Cache 留足余量,否则 compaction 时会产生剧烈的 IO 抖动。
说到 compaction,这是 TiKV 运维中最头疼的问题之一。当 L0 层 SST 文件堆积过多,RocksDB 会触发 Write Stall,导致整个 TiKV 写入暂停,上游 TiDB Server 报错 tikv server timeout。避免 Write Stall 的关键是控制写入放大,具体手段包括:调大 rocksdb.defaultcf.level0-slowdown-writes-trigger 到 40 左右,调大 rocksdb.defaultcf.level0-stop-writes-trigger 到 60 左右,同时适当增加 rocksdb.defaultcf.max-bytes-for-level-base 到 512MB 或 1GB。但根本解决之道还是业务侧的写入流量削峰,或者用 TiCDC 做异步解耦,把突发写入先落到 Kafka 再消费进 TiDB。
TiFlash 的引入让 TiDB 具备了 HTAP 能力,但部署 TiFlash 后,很多用户发现 TP 业务延迟反而升高了。原因在于 TiFlash 同步数据时,会从 TiKV 拉取 Raft Log,如果 TiFlash 节点网络带宽不足或磁盘写入慢,会导致 Raft Log 堆积,反过来拖慢 TiKV 的 commit 速度。解决办法是给 TiFlash 独立网卡或万兆网络,并且 raftstore.store-io-pool-size 要为 TiFlash 同步预留专门的 IO 线程,避免与 TiKV 自身的 Raft IO 争抢。另外,TiFlash 的 profiles.default.dt_enable_logical_async_write 参数建议开启,能显著降低同步延迟。
备份恢复不是简单的 br backup full 就万事大吉。BR 工具在备份时会对集群加全局读锁,虽然时间极短,但在高频写入场景下仍会造成瞬间的写入卡顿。生产环境必须使用 --ratelimit 限制备份带宽,避免打满 TiKV 的 gRPC 线程池。恢复时,br restore 默认并发度很高,会把 TiKV 的 CPU 和 IO 瞬间吃满,导致在线业务不可用。正确做法是分批次恢复,用 --concurrency 控制并行度,并且在业务低峰期操作。更推荐的做法是开启 PiTR 日志备份,配合全量备份实现分钟级 RPO,这样即使误删数据,也能通过 br restore point 回滚到指定时间点,而不是全量恢复整个集群。
监控体系必须分层建设。Prometheus + Grafana 是标配,但 Overview 面板信息太泛,真正排障要看 TiKV-Details 里的 Scheduler 和 Raft IO 面板。如果 scheduler writing duration 持续超过 100ms,说明写入链路有瓶颈;如果 raftstore propose wait duration 飙升,说明 Raft 共识卡顿,大概率是磁盘 fsync 延迟过高。另外,一定要配置 Blackbox Exporter 做 TiDB Server 的 SQL 端口拨测,因为 TiDB Server 进程活着不代表能正常响应 SQL,连接池满或内存溢出时,端口通但查询全部超时,这种故障 Prometheus 的进程监控根本发现不了。
升级版本是高风险操作,但很多团队直接 tiup cluster upgrade 一把梭,这是非常危险的。TiDB 的升级本质是滚动重启,先升级 PD,再 TiKV,最后 TiDB Server 和 TiFlash。关键风险点在于:升级 TiKV 时,每个 TiKV 节点重启都会触发 Leader 迁移,如果集群 Region 数量巨大,Leader 迁移会产生大量 Raft 消息,可能把网络带宽打满。所以升级前必须用 tiup ctl pd region --format=table | wc -l 检查 Region 数量,超过 50 万时,必须调大 raft-base-tick-interval 降低心跳频率,并且分批灰度升级,每批间隔至少 10 分钟,观察 Leader 均衡完成后再继续。
最后说一个容易被忽视的配置:GC 生命周期。TiDB 默认 tidb_gc_life_time 是 10 分钟,这意味着 MVCC 旧版本数据只保留 10 分钟。如果业务有长时间运行的只读事务,或者需要做跨长时间范围的数据比对,10 分钟后旧版本被 GC 清理,查询就会报 GC life time is shorter than transaction duration。建议根据最长事务时间调整到 1 小时甚至 24 小时,但要同步调大 tikv_gc.max-write-bytes-per-sec,否则 GC 清理速度跟不上写入速度,会导致磁盘空间持续增长。另外,GC 的 safe point 必须监控,一旦 safe point 长时间不推进,说明有长事务阻塞 GC,需要立即 kill 掉对应会话,否则集群存储空间会被撑爆。
TiDB 的运维深度远不止这些,但以上每一条都是生产环境血泪教训换来的。分布式数据库的复杂性不在于功能多少,而在于故障域被放大后,一个参数配置失误就可能引发雪崩。真正掌握 TiDB,不是会装会用,而是能根据业务特征,把每一层参数调教到与硬件和负载精确匹配的状态。
