本文介绍了基于数据库时间的系统优化方法,以及如何利用 TiDB Performance Overview 面板进行性能分析和优化。
通过本文中介绍的方法,你可以从全局、自顶向下的角度分析用户响应时间和数据库时间,确认用户响应时间的瓶颈是否在数据库中。如果瓶颈在数据库中,你可以通过数据库时间概览和 SQL 延迟的分解,定位数据库内部的瓶颈点,并进行针对性的优化。
TiDB 对 SQL 的处理路径和数据库时间进行了完善的测量和记录,方便定位数据库的性能瓶颈。即使在用户响应时间的性能数据缺失的情况下,基于 TiDB 数据库时间的相关性能指标,你也可以达到以下两个性能分析目标:
一个典型的 SQL 的处理流程如下所示,TiDB 的性能指标覆盖了绝大部分的处理路径,对数据库时间进行不同维度的分解和上色,用户可以快速的了解负载特性和数据库内部的瓶颈。
数据库时间是所有 SQL 处理时间的总和。通过以下三个维度对数据库时间进行分解,可以帮助你快速定位 TiDB 内部瓶颈:
DB Time = Select Time + Insert Time + Update Time + Delete Time + Commit Time + ...
DB Time = Get Token Time + Parse Time + Comiple Time + Execute Time
Execute Time ~= TiDB Executor Time + KV Request Time + PD TSO Wait Time + Retried execution time
本章介绍如何利用 Grafana 中的 Performance Overview 面板进行基于数据库时间的性能分析和优化。
Performance Overview 面板按总分结构对 TiDB、TiKV、PD 的性能指标进行编排组织,包括以下三个部分:
Database Time 指标为 TiDB 每秒处理 SQL 的延迟总和,即 TiDB 集群每秒并发处理应用 SQL 请求的总时间(等于活跃连接数)。
Performance Overview 面板提供了以下三个面积堆叠图,帮助你了解数据库负载的类型,快速定位数据库时间的瓶颈主要是处理什么语句,集中在哪个执行阶段,SQL 执行阶段主要等待 TiKV 或者 PD 哪种请求类型。
通过观察数据库时间分解图和执行时间概览图,你可以直观地区分正常或者异常的时间消耗,快速定位集群的异常瓶颈点,高效了解集群的负载特征。对于正常的时间消耗和请求类型,图中显示颜色为绿色系或蓝色系。如果非绿色或蓝色系的颜色在这两张图中占据了明显的比例,意味着数据库时间的分布不合理。
示例 1:TPC-C 负载
注意
Retried execution time
过长。示例 2:OLTP 读密集负载
示例 3:只读 OLTP 负载
注意
示例 3 select 语句需要从多个 TiKV 并行读取几千行数据,BatchGet 请求的总时间远大于执行时间。
示例 4: 锁争用负载
Retried execution time
过长。目前 Retried execution time
消耗的时间,TiDB 尚未进行测量。通过观察 Performance Overview 里的以下三个面板,可以了解应用的负载类型,与 TiDB 的交互方式,以及是否能有效地利用 TiDB 的执行计划缓存。
示例 1:TPC-C 负载
TPC-C 负载类型主要以 Update、Select 和 Insert 语句为主。总的 QPS 等于每秒 StmtExecute 的次数,并且 StmtExecute 每秒的数据基本等于 Queries Using Plan Cache OPS。这是 OLTP 负载理想的情况,客户端执行使用 prepared statement,并且在客户端缓存了 prepared statement 对象,执行每条 SQL 语句时直接调用 statement 执行。执行时都命中执行计划缓存,不需要重新 compile 生成执行计划。
示例 2:只读 OLTP 负载,使用 query 命令无法使用执行计划缓存
这个负载中, Commit QPS = Rollback QPS = Select QPS。应用开启了 auto-commit 并发,每次从连接池获取连接都会执行 rollback,因此这三种语句的执行次数是相同的。
示例 3:OLTP 负载,使用 prepared statement 接口无法使用执行计划缓存
StmtPreare 次数 = StmtExecute 次数 = StmtClose 次数 ~= StmtFetch 次数,应用使用了 prepare > execute > fetch > close 的 loop,很多框架都会在 execute 之后调用 close,确保资源不会泄露。这会带来两个问题:
注意
从 TiDB v6.0.0 起,你可以通过全局变量 (set global tidb_ignore_prepared_cache_close_stmt=on;
) 控制 StmtClose 命令不清理已被缓存的执行计划,使得下一次的 SQL 的执行不需要重新生成执行计划。
在 KV/TSO Request OPS 面板中,你可以查看 KV 和 TSO 每秒请求的数据统计。其中,kv request total
代表 TiDB 到 TiKV 所有请求的总和。通过观察 TiDB 到 PD 和 TiKV 的请求类型,可以了解集群内部的负载特征。
在 Connection Count (连接信息)面板中,你可以查看总的连接数和每个 TiDB 的连接数,并由此判断连接总数是否正常,每个 TiDB 的连接数是否不均衡。active connections
记录着活跃连接数,等于每秒的数据库时间。
示例 1:繁忙的负载
在此 TPC-C 负载中:
PessimisticsLock
、Prewrite
、Commit
和 BatchGet
等。示例 2:空闲的负载
在此负载中:
在 TiDB CPU 和 TiKV CPU/IO MBps 这两个面板中,你可以观察到 TiDB 和 TiKV 的逻辑 CPU 使用率和 IO 吞吐,包含平均、最大和 delta(最大 CPU 使用率减去最小 CPU 使用率),从而用来判定 TiDB 和 TiKV 总体的 CPU 使用率。
delta
值,你可以判断 TiDB 是否存在 CPU 使用负载不均衡(通常伴随着应用连接不均衡),TiKV 是否存在热点。示例 1:TiDB 资源使用率高
下图负载中,每个 TiDB 和 TiKV 配置 8 CPU。
由此可以判断,TiDB 的 CPU 消耗明显更高,并接近于 8 CPU 的瓶颈,可以考虑扩容 TiDB。
示例 2:TiKV 资源使用率高
下图 TPC-C 负载中,每个 TiDB 和 TiKV 配置 16 CPU。
由此可以判断,TiKV 的 CPU 消耗更高,因为 TPC-C 是一个写密集场景,这是正常现象,可以考虑扩容 TiKV 节点提升性能。
延迟面板通常包含平均值和 99 分位数,平均值用来定位总体的瓶颈,99 分位数用来判断是否存在延迟严重抖动的情况。判断性能抖动范围时,可能还需要需要借助 999 分位数。
Duration 面板包含了所有语句的 99 延迟和每种 SQL 类型的平均延迟。Connection Idle Duration 面板包含连接空闲的平均和 99 延迟,连接空闲时包含两种状态:
应用进行数据库事务时,通常使用同一个数据库连接。对比 query 的平均延迟和 connection idle duration 的延迟,可以判断整个系统性能瓶颈或者用户响应时间的抖动是否是由 TiDB 导致的。
avg-in-txn
可以判断应用处理事务时,主要的时间是花在数据库内部还是在数据库外面,借此定位用户响应时间的瓶颈。avg-in-txn
指标,可以对比 query 平均延迟和 avg-not-in-txn
指标。现实的客户负载中,瓶颈在数据库外面的情况并不少见,例如:
示例 1:用户响应时间的瓶颈在 TiDB 中
在此 TPC-C 负载中:
avg-in-txn
171 us。由此可以判断,平均的 query 延迟明显大于 avg-in-txn
,说明事务处理中,主要的瓶颈在数据库内部。
示例 2:用户响应时间的瓶颈不在 TiDB 中
在此负载中,平均 query 延迟为 1.69ms,事务中连接空闲时间 avg-in-txn
为 18ms。说明事务中,TiDB 平均花了 1.69ms 处理完一个 SQL 语句之后,需要等待 18ms 才能收到下一条语句。
由此可以判断,用户响应时间的瓶颈不在 TiDB 中。这个例子是在一个公有云环境下,因为应用和数据库不在同一个地区,应用和数据库之间的网络延迟高导致了超高的连接空闲时间。
在 TiDB 中,从输入查询文本到返回结果的典型处理流程。
SQL 在 TiDB 内部的处理分为四个阶段,get token、parse、compile 和 execute:
如果应用统一使用 query 或者 StmtExecute MySQL 命令接口,可以使用以下公式来定位平均延迟的瓶颈。
avg Query Duration = avg Get Token + avg Parse Duration + avg Compile Duration + avg Execute Duration
通常 execute 阶段会占 query 延迟的主要部分,在以下情况下,parse 和 compile 阶段也会占比明显。
Go
编写的),进一步影响 TiDB 其他组件的性能。这说明,OLTP 负载在 TiDB 中是否能高效运行,执行计划缓扮演了重要的角色。示例 1:数据库瓶颈在 compile 阶段
此图中 parse、compile 和 execute 阶段的平均时间分别为 17.1 us、729 us 和 681 us。因为应用使用 query 命令接口,无法使用执行计划缓存,所以 compile 阶段延迟高。
示例 2:数据库瓶颈在 execute 阶段
在此 TPC-C 负载中,parse、compile 和 execute 阶段的平均时间分别为 7.39us、38.1us 和 12.8ms。query 延迟的瓶颈在于 execute 阶段。
在 execute 阶段,TiDB 会跟 PD 和 TiKV 进行交互。如下图所示,当 TiDB 处理 SQL 语句请求时,在进行 parse 和 compile 之前,如果需要获取 TSO,会先请求生成 TSO。PD Client 不会阻塞调用者,而是直接返回一个 TSFuture
,并在后台异步处理 TSO 请求的收发,一旦完成立即返回给 TSFuture,TSFuture 的持有者则需要调用 Wait 方法来获得最终的 TSO 结果。当 TiDB 完成 parse 和 compile 之后, 进入 execute 阶段,此时存在两个情况:
TSO 等待的时间记录为 TSO WAIT,TSO 请求的网络时间记录为 TSO RPC。TiDB TSO 等待完成之后,执行过程中通常需要和 TiKV 进行读写交互:
这一部分的指标对应以下三个面板:
其中,Avg TiDB KV Request Duration 和 Avg TiKV GRPC Duration 的关系如下
Avg TiDB KV Request Duration = Avg TiKV GRPC Duration + TiDB 与 TiKV 之间的网络延迟 + TiKV GRPC 处理时间 + TiDB GRPC 处理时间和调度延迟。
Avg TiDB KV Request Duration 和 Avg TiKV GRPC Duration 的差值跟网络流量和延迟,TiDB 和 TiKV 的资源使用情况密切相关。
示例 1:同机器低负载的集群
在此负载中,TiDB 侧平均 Prewrite 请求延迟为 925 us,TiKV 内部 kv_prewrite 平均处理延迟为 720 us,相差 200 us 左右,是同机房内正常的延迟。TSO wait 平均延迟 206 us,rpc 时间为 144 us。
示例 2:公有云集群,负载正常
在此示例中,TiDB 集群部署在同一个地区的不同机房。TiDB 侧平均 Commit 请求延迟为 12.7 ms,TiKV 内部 kv_commit 平均处理延迟为 10.2ms,相差 2.5ms 左右。TSO wait 平均延迟为 3.12ms,rpc 时间为 693us。
示例 3:公有云集群,资源严重过载
在此示例中,TiDB 集群部署在同一个地区的不同机房,TiDB 网络和 CPU 资源严重过载。TiDB 侧平均 BatchGet 请求延迟为 38.6 ms,TiKV 内部 kv_batch_get 平均处理延迟为 6.15 ms,相差超过 32 ms,远高于正常值。TSO wait 平均延迟为 9.45ms,rpc 时间为 14.3ms。
TiKV 对于写请求的处理流程如下图
scheduler worker
会先处理写请求,进行事务一致性检查,并把写请求转化成键值对,发送到 raftstore
模块。raftstore
为 TiKV 的共识模块,使用 Raft 共识算法,使多个 TiKV 组成的存储层可以容错。
Raftstore 分为 store 线程和 apply 线程。:
proposals
。 当收到新的 proposals
时,leader 节点的 store 线程会写入本地 Raft DB,并将消息复制到多个 follower 节点。当这个 proposals
在多数实例持久化成功之后,proposals
成功被提交。Storage Async Write Duration 指标记录写请求进入 raftstore 之后的延迟,采集的粒度具体到每个请求的级别。
Storage Async Write Duration 分为 Store Duration 和 Apply Duration。你可以通过以下公式定位写请求的瓶颈主要是 在 Store 还是 Apply 步骤。
avg Storage Async Write Duration = avg Store Duration + avg Apply Duration
注意
Store Duration 和 Apply Duration 从 v5.3.0 版本开始支持。
示例 1:同一个 OLTP 负载在 v5.3.0 和 v5.4.0 版本的对比
v5.4.0 版本,一个写密集的 OLTP 负载 QPS 比 v5.3.0 提升了 14%。应用以上公式
因为 v5.4.0 版本中,TiKV 对 gRPC 模块进行了优化,优化了 Raft 日志复制速度, 相比 v5.3.0 降低了 Store Duration。
v5.3.0:
v5.4.0:
示例 2:Store Duration 瓶颈明显
应用以上公式:10.1 ms ~= 9.81 ms + 0.304 ms,说明写请求的延迟瓶颈在 Store Duration。
Commit Log Duration、Append Log Duration 和 Apply Log Duration 这三个延迟是 raftstore 内部关键操作的延迟记录。这些记录采集的粒度是 batch 操作级别的,每个操作会把多个写请求合并在一起,因此不能直接对应上文的 Store Duration 和 Apply Duration。
Commit Log Duration 慢的常见场景:
raftstore.store-pool-size
设置过小或者过大(过大也可能导致性能下降)Apply Log Duration 慢的常见场景:
raftstore.apply-pool-size
设置过小或者过大(过大也可能导致性能下降)示例 1:同一个 OLTP 负载在 v5.3.0 和 v5.4.0 版本的对比
v5.4.0 版本,一个写密集的 OLTP 负载 QPS 比 v5.3.0 提升了 14%。 对比这三个关键延迟:
Avg Duration | v5.3.0(ms) | v5.4.0(ms) |
---|---|---|
Append Log Duration | 0.27 | 0.303 |
Commit Log Duration | 13 | 8.68 |
Apply Log Duration | 0.457 | 0.514 |
因为 v5.4.0 版本中,TiKV 对 gRPC 模块进行了优化,优化了 Raft 日志复制速度, 相比 v5.3.0 降低了 Commit Log Duration 和 Store Duration。
v5.3.0:
v5.4.0:
示例 2:Commit Log Duration 瓶颈明显的例子
Store 线程的 Commit Log Duration 明显比 Apply Log Duration 高,并且 Append Log Duration 比 Apply Log Duration 明显的高,说明 Store 线程在 CPU 和 IO 都可能都存在瓶颈。可能降低 Commit Log Duration 和 Append Log Duration 的方式如下:
raftstore.store-pool-size
。Raft Engine
,Raft Engine 具有更轻量的执行路径,在一些场景下显著减少 IO 写入量和写入请求的长尾延迟,启用方式为设置:raft-engine.enable: true
StoreWriter
。启用方式:raftstore.store-io-pool-size: 1
。从 v6.1.0 起,TiDB Grafana 组件默认内置了 Performance Overview 面板。Performance overview 面板兼容 TiDB v4.x 和 v5.x 版本。如果你的 TiDB 版本低于 v6.1.0,需要手动导入 performance_overview.json
。
导入方法如图所示:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。