- 关于 TiDB
- 快速上手
- 部署标准集群
- 数据迁移
- 运维操作
- 监控与告警
- 故障诊断
- 性能调优
- 系统调优
- 软件调优
- SQL 性能调优
- SQL 性能调优概览
- 理解 TiDB 执行计划
- SQL 优化流程
- 控制执行计划
- 教程
- 同城多中心部署
- 两地三中心部署
- 同城两中心部署
- 读取历史数据
- 使用 Stale Read 功能读取历史数据(推荐)
- 使用系统变量
tidb_snapshot
读取历史数据
- 最佳实践
- Placement Rules 使用文档
- Load Base Split 使用文档
- Store Limit 使用文档
- TiDB 工具
- 功能概览
- 适用场景
- 工具下载
- TiUP
- 文档地图
- 概览
- 术语及核心概念
- TiUP 组件管理
- FAQ
- 故障排查
- TiUP 命令参考手册
- 命令概览
- TiUP 命令
- TiUP Cluster 命令
- TiUP Cluster 命令概览
- tiup cluster audit
- tiup cluster check
- tiup cluster clean
- tiup cluster deploy
- tiup cluster destroy
- tiup cluster disable
- tiup cluster display
- tiup cluster edit-config
- tiup cluster enable
- tiup cluster help
- tiup cluster import
- tiup cluster list
- tiup cluster patch
- tiup cluster prune
- tiup cluster reload
- tiup cluster rename
- tiup cluster replay
- tiup cluster restart
- tiup cluster scale-in
- tiup cluster scale-out
- tiup cluster start
- tiup cluster stop
- tiup cluster template
- tiup cluster upgrade
- TiUP DM 命令
- TiUP DM 命令概览
- tiup dm audit
- tiup dm deploy
- tiup dm destroy
- tiup dm disable
- tiup dm display
- tiup dm edit-config
- tiup dm enable
- tiup dm help
- tiup dm import
- tiup dm list
- tiup dm patch
- tiup dm prune
- tiup dm reload
- tiup dm replay
- tiup dm restart
- tiup dm scale-in
- tiup dm scale-out
- tiup dm start
- tiup dm stop
- tiup dm template
- tiup dm upgrade
- TiDB 集群拓扑文件配置
- DM 集群拓扑文件配置
- TiUP 镜像参考指南
- TiUP 组件文档
- PingCAP Clinic 诊断服务 (Technical Preview)
- TiDB Operator
- Dumpling
- TiDB Lightning
- TiDB Data Migration
- 关于 Data Migration
- 快速开始
- 部署 DM 集群
- 入门指南
- 进阶教程
- 运维管理
- 参考手册
- 使用示例
- 异常解决
- 版本发布历史
- Backup & Restore (BR)
- TiDB Binlog
- TiCDC
- sync-diff-inspector
- TiSpark
- 参考指南
- 架构
- 监控指标
- 安全加固
- 权限
- SQL
- SQL 语言结构和语法
- SQL 语句
ADD COLUMN
ADD INDEX
ADMIN
ADMIN CANCEL DDL
ADMIN CHECKSUM TABLE
ADMIN CHECK [TABLE|INDEX]
ADMIN SHOW DDL [JOBS|QUERIES]
ADMIN SHOW TELEMETRY
ALTER DATABASE
ALTER INDEX
ALTER INSTANCE
ALTER PLACEMENT POLICY
ALTER TABLE
ALTER USER
ANALYZE TABLE
BACKUP
BEGIN
CHANGE COLUMN
CHANGE DRAINER
CHANGE PUMP
COMMIT
CREATE [GLOBAL|SESSION] BINDING
CREATE DATABASE
CREATE INDEX
CREATE PLACEMENT POLICY
CREATE ROLE
CREATE SEQUENCE
CREATE TABLE LIKE
CREATE TABLE
CREATE USER
CREATE VIEW
DEALLOCATE
DELETE
DESC
DESCRIBE
DO
DROP [GLOBAL|SESSION] BINDING
DROP COLUMN
DROP DATABASE
DROP INDEX
DROP PLACEMENT POLICY
DROP ROLE
DROP SEQUENCE
DROP STATS
DROP TABLE
DROP USER
DROP VIEW
EXECUTE
EXPLAIN ANALYZE
EXPLAIN
FLASHBACK TABLE
FLUSH PRIVILEGES
FLUSH STATUS
FLUSH TABLES
GRANT <privileges>
GRANT <role>
INSERT
KILL [TIDB]
LOAD DATA
LOAD STATS
MODIFY COLUMN
PREPARE
RECOVER TABLE
RENAME INDEX
RENAME TABLE
REPLACE
RESTORE
REVOKE <privileges>
REVOKE <role>
ROLLBACK
SELECT
SET DEFAULT ROLE
SET [NAMES|CHARACTER SET]
SET PASSWORD
SET ROLE
SET TRANSACTION
SET [GLOBAL|SESSION] <variable>
SHOW [BACKUPS|RESTORES]
SHOW ANALYZE STATUS
SHOW [GLOBAL|SESSION] BINDINGS
SHOW BUILTINS
SHOW CHARACTER SET
SHOW COLLATION
SHOW [FULL] COLUMNS FROM
SHOW CONFIG
SHOW CREATE PLACEMENT POLICY
SHOW CREATE SEQUENCE
SHOW CREATE TABLE
SHOW CREATE USER
SHOW DATABASES
SHOW DRAINER STATUS
SHOW ENGINES
SHOW ERRORS
SHOW [FULL] FIELDS FROM
SHOW GRANTS
SHOW INDEX [FROM|IN]
SHOW INDEXES [FROM|IN]
SHOW KEYS [FROM|IN]
SHOW MASTER STATUS
SHOW PLACEMENT
SHOW PLACEMENT FOR
SHOW PLACEMENT LABELS
SHOW PLUGINS
SHOW PRIVILEGES
SHOW [FULL] PROCESSSLIST
SHOW PROFILES
SHOW PUMP STATUS
SHOW SCHEMAS
SHOW STATS_HEALTHY
SHOW STATS_HISTOGRAMS
SHOW STATS_META
SHOW STATUS
SHOW TABLE NEXT_ROW_ID
SHOW TABLE REGIONS
SHOW TABLE STATUS
SHOW [FULL] TABLES
SHOW [GLOBAL|SESSION] VARIABLES
SHOW WARNINGS
SHUTDOWN
SPLIT REGION
START TRANSACTION
TABLE
TRACE
TRUNCATE
UPDATE
USE
WITH
- 数据类型
- 函数与操作符
- 聚簇索引
- 约束
- 生成列
- SQL 模式
- 表属性
- 事务
- 垃圾回收 (GC)
- 视图
- 分区表
- 临时表
- 缓存表
- 字符集和排序
- Placement Rules in SQL
- 系统表
mysql
- INFORMATION_SCHEMA
- Overview
ANALYZE_STATUS
CLIENT_ERRORS_SUMMARY_BY_HOST
CLIENT_ERRORS_SUMMARY_BY_USER
CLIENT_ERRORS_SUMMARY_GLOBAL
CHARACTER_SETS
CLUSTER_CONFIG
CLUSTER_HARDWARE
CLUSTER_INFO
CLUSTER_LOAD
CLUSTER_LOG
CLUSTER_SYSTEMINFO
COLLATIONS
COLLATION_CHARACTER_SET_APPLICABILITY
COLUMNS
DATA_LOCK_WAITS
DDL_JOBS
DEADLOCKS
ENGINES
INSPECTION_RESULT
INSPECTION_RULES
INSPECTION_SUMMARY
KEY_COLUMN_USAGE
METRICS_SUMMARY
METRICS_TABLES
PARTITIONS
PLACEMENT_POLICIES
PROCESSLIST
REFERENTIAL_CONSTRAINTS
SCHEMATA
SEQUENCES
SESSION_VARIABLES
SLOW_QUERY
STATISTICS
TABLES
TABLE_CONSTRAINTS
TABLE_STORAGE_STATS
TIDB_HOT_REGIONS
TIDB_HOT_REGIONS_HISTORY
TIDB_INDEXES
TIDB_SERVERS_INFO
TIDB_TRX
TIFLASH_REPLICA
TIKV_REGION_PEERS
TIKV_REGION_STATUS
TIKV_STORE_STATUS
USER_PRIVILEGES
VIEWS
METRICS_SCHEMA
- UI
- CLI
- 命令行参数
- 配置文件参数
- 系统变量
- 存储引擎
- 遥测
- 错误码
- 通过拓扑 label 进行副本调度
- 常见问题解答 (FAQ)
- 版本发布历史
- 术语表
海量 Region 集群调优最佳实践
在 TiDB 的架构中,所有数据以一定 key range 被切分成若干 Region 分布在多个 TiKV 实例上。随着数据的写入,一个集群中会产生上百万个甚至千万个 Region。单个 TiKV 实例上产生过多的 Region 会给集群带来较大的负担,影响整个集群的性能表现。
本文将介绍 TiKV 核心模块 Raftstore 的工作流程,海量 Region 导致性能问题的原因,以及优化性能的方法。
Raftstore 的工作流程
一个 TiKV 实例上有多个 Region。Region 消息是通过 Raftstore 模块驱动 Raft 状态机来处理的。这些消息包括 Region 上读写请求的处理、Raft log 的持久化和复制、Raft 的心跳处理等。但是,Region 数量增多会影响整个集群的性能。为了解释这一点,需要先了解 TiKV 的核心模块 Raftstore 的工作流程。
该图仅为示意,不代表代码层面的实际结构。
上图是 Raftstore 处理流程的示意图。如图所示,从 TiDB 发来的请求会通过 gRPC 和 storage 模块变成最终的 KV 读写消息,并被发往相应的 Region,而这些消息并不会被立即处理而是被暂存下来。Raftstore 会轮询检查每个 Region 是否有需要处理的消息。如果 Region 有需要处理的消息,那么 Raftstore 会驱动 Raft 状态机去处理这些消息,并根据这些消息所产生的状态变更去进行后续操作。例如,在有写请求时,Raft 状态机需要将日志落盘并且将日志发送给其他 Region 副本;在达到心跳间隔时,Raft 状态机需要将心跳信息发送给其他 Region 副本。
性能问题
从 Raftstore 处理流程示意图可以看出,需要依次处理各个 Region 的消息。那么在 Region 数量较多的情况下,Raftstore 需要花费一些时间去处理大量 Region 的心跳,从而带来一些延迟,导致某些读写请求得不到及时处理。如果读写压力较大,Raftstore 线程的 CPU 使用率容易达到瓶颈,导致延迟进一步增加,进而影响性能表现。
通常在有负载的情况下,如果 Raftstore 的 CPU 使用率达到了 85% 以上,即可视为达到繁忙状态且成为了瓶颈,同时 propose wait duration
可能会高达百毫秒级别。
- Raftstore 的 CPU 使用率是指单线程的情况。如果是多线程 Raftstore,可等比例放大使用率。
- 由于 Raftstore 线程中有 I/O 操作,所以 CPU 使用率不可能达到 100%。
性能监控
可在 Grafana 的 TiKV 面板下查看相关的监控 metrics:
Thread-CPU 下的
Raft store CPU
参考值:低于
raftstore.store-pool-size * 85%
。Raft Propose 下的
Propose wait duration
Propose wait duration
是从发送请求给 Raftstore,到 Raftstore 真正开始处理请求之间的延迟时间。如果该延迟时间较长,说明 Raftstore 比较繁忙或者处理 append log 比较耗时导致 Raftstore 不能及时处理请求。参考值:低于 50-100ms。
性能优化方法
找到性能问题的根源后,可从以下两个方向来解决性能问题:
- 减少单个 TiKV 实例的 Region 数
- 减少单个 Region 的消息数
方法一:增加 TiKV 实例
如果 I/O 资源和 CPU 资源都比较充足,可在单台机器上部署多个 TiKV 实例,以减少单个 TiKV 实例上的 Region 个数;或者增加 TiKV 集群的机器数。
方法二:调整 raft-base-tick-interval
除了减少 Region 个数外,还可以通过减少 Region 单位时间内的消息数量来减小 Raftstore 的压力。例如,在 TiKV 配置中适当调大 raft-base-tick-interval
:
[raftstore]
raft-base-tick-interval = "2s"
raft-base-tick-interval
是 Raftstore 驱动每个 Region 的 Raft 状态机的时间间隔,也就是每隔该时长就需要向 Raft 状态机发送一个 tick 消息。增加该时间间隔,可以有效减少 Raftstore 的消息数量。
需要注意的是,该 tick 消息的间隔也决定了 election timeout
和 heartbeat
的间隔。示例如下:
raft-election-timeout = raft-base-tick-interval * raft-election-timeout-ticks
raft-heartbeat-interval = raft-base-tick-interval * raft-heartbeat-ticks
如果 Region Follower 在 raft-election-timeout
间隔内未收到来自 Leader 的心跳,就会判断 Leader 出现故障而发起新的选举。raft-heartbeat-interval
是 Leader 向 Follower 发送心跳的间隔,因此调大 raft-base-tick-interval
可以减少单位时间内 Raft 发送的网络消息,但也会让 Raft 检测到 Leader 故障的时间更长。
方法三:提高 Raftstore 并发数
从 v3.0 版本起,Raftstore 已经扩展为多线程,极大降低了 Raftstore 线程成为瓶颈的可能性。
TiKV 默认将 raftstore.store-pool-size
配置为 2
。如果 Raftstore 出现瓶颈,可以根据实际情况适当调高该参数值,但不建议设置过高以免引入不必要的线程切换开销。
方法四:开启 Hibernate Region 功能
在实际情况中,读写请求并不会均匀分布到每个 Region 上,而是集中在少数的 Region 上。那么可以尽量减少暂时空闲的 Region 的消息数量,这也就是 Hibernate Region 的功能。无必要时可不进行 raft-base-tick
,即不驱动空闲 Region 的 Raft 状态机,那么就不会触发这些 Region 的 Raft 产生心跳信息,极大地减小了 Raftstore 的工作负担。
Hibernate Region 在 TiKV master 分支上默认开启。可根据实际情况和需求来配置此功能的开启和关闭,请参阅配置 Hibernate Region。
方法五:开启 Region Merge
从 TiDB v3.0 开始,Region Merge
默认开启。
开启 Region Merge
也能减少 Region 的个数。与 Region Split
相反,Region Merge
是通过调度把相邻的小 Region 合并的过程。在集群中删除数据或者执行 Drop Table
/Truncate Table
语句后,可以将小 Region 甚至空 Region 进行合并以减少资源的消耗。
通过 pd-ctl 设置以下参数即可开启 Region Merge
:
>> pd-ctl config set max-merge-region-size 20
>> pd-ctl config set max-merge-region-keys 200000
>> pd-ctl config set merge-schedule-limit 8
详情请参考如何配置 Region Merge(英文) 和 PD 配置文件描述。
同时,默认配置的 Region Merge
的参数设置较为保守,可以根据需求参考 PD 调度策略最佳实践中提供的方法加快 Region Merge
过程的速度。
其他问题和解决方案
切换 PD Leader 的速度慢
PD 需要将 Region Meta 信息持久化在 etcd 上,以保证切换 PD Leader 节点后 PD 能快速继续提供 Region 路由服务。随着 Region 数量的增加,etcd 出现性能问题,使得 PD 在切换 Leader 时从 etcd 获取 Region Meta 信息的速度较慢。在百万 Region 量级时,从 etcd 获取信息的时间可能需要十几秒甚至几十秒。
因此从 v3.0 版本起,PD 默认开启配置项 use-region-storage
,将 Region Meta 信息存在本地的 LevelDB 中,并通过其他机制同步 PD 节点间的信息。
PD 路由信息更新不及时
在 TiKV 中,pd-worker 模块将 Region Meta 信息定期上报给 PD,在 TiKV 重启或者切换 Region Leader 时需要通过统计信息重新计算 Region 的 approximate size/keys
。因此在 Region 数量较多的情况下,pd-worker 单线程可能成为瓶颈,造成任务得不到及时处理而堆积起来。因此 PD 不能及时获取某些 Region Meta 信息以致路由信息更新不及时。该问题不会影响实际的读写,但可能导致 PD 调度不准确以及 TiDB 更新 Region cache 时需要多几次 round-trip。
可在 TiKV Grafana 面板中查看 Task 下的 Worker pending tasks 来确定 pd-worker 是否有任务堆积。通常来说,pending tasks 应该维持在一个比较低的值。
老版本 TiDB (< v3.0.5) pd-worker 的效率有一些缺陷,如果碰到类似问题,建议升级至最新版本。
Prometheus 查询 metrics 的速度慢
在大规模集群中,随着 TiKV 实例数的增加,Prometheus 查询 metrics 时的计算压力较大,导致 Grafana 查看 metrics 的速度较慢。从 v3.0 版本起设置了一些 metrics 的预计算,让这个问题有所缓解。