Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add troubleshoot-high-disk-io.md #3237

Merged
merged 17 commits into from
May 26, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions troubleshoot-high-disk-io.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
title: TiDB 磁盘 I/O 过高的处理办法
summary: 了解如何定位和处理 TiDB 存储 I/O 过高的问题。
category: reference
---

# TiDB 磁盘 I/O 过高的处理办法

在使用 TiDB 数据库时,磁盘 I/O 瓶颈是绕不开的问题,本文介绍如何定位和处理 TiDB 存储 I/O 过高的问题。

## 确认当前 I/O 指标

当出现系统响应变慢的时候,如果已经排查了 CPU 的瓶颈、数据事务冲突的瓶颈后,就需要从 I/O 来入手来辅助判断目前的系统瓶颈点。

### 从监控定位 I/O 问题

最快速的定位手段是从监控来查看整体的 I/O 情况,可以从集群部署工具 (TiDB-Ansible, TiUP) 默认会部署的监控组件 Grafana 来查看对应的 I/O 监控,跟 I/O 相关的 Dashboard 有 `Overview`,`Node_exporter`,`Disk-Performance`。

#### 第一类面板

在 `Overview` > `System Info` > `IO Util` 中,可以看到集群中每个机器的 I/O 情况,该指标和 Linux iostat 监控中的 util 类似,百分比越高代表磁盘 I/O 占用越高:

- 如果监控中只有一台机器的 I/O 高,那么可以辅助判断当前有读写热点。
- 如果监控中大部分机器的 I/O 都高,那么集群现在有高 I/O 负载存在。

如果发现某台机器的 I/O 比较高,可以从监控 `Disk-Performance Dashboard` 进一步观察 I/O 的使用情况,结合 `Disk Latency`,`Disk Load` 等 metric 判断是否存在异常,必要时可以使用 fio 工具对磁盘进行检测。

#### 第二类面板

TiDB 集群主要的持久化组件是 TiKV 集群,一个 TiKV 包含两个 RocksDB 实例:一个用于存储 Raft 日志,位于 data/raft,一个用于存储真正的数据,位于 data/db。

在 `TiKV-Details` > `Raft IO` 中,可以看到这两个实例磁盘写入的相关 metric:

- `Append log duration`:该监控表明了存储 Raft 日志的 RocksDB 写入的响应时间,.99 响应应该在 50ms 以内。
- `Apply log duration`:该监控表明了存储真正数据的 RocksDB 写入的响应时间,.99 响应应该在 100ms 以内。

这两个监控还有 `.. per server` 的监控面板来提供辅助查看热点写入的情况。

#### 第三类面板

在 `TiKV-Details` > `Storage` 中,有关于 storage 相关情况的监控:

- `Storage command total`:收到的不同命令的个数。
- `Storage async write duration`:包括了磁盘 sync duration 等监控项,可能和 Raft IO 有关。如遇到异常情况,需要通过 log 来检查相关组件的工作状态是否正常。

#### 其他面板

此外,可能还需要一些其它内容来辅助确认瓶颈是否为 I/O,并可以尝试调整一些参数。通过查看 TiKV gRPC 的 prewrite/commit/raw-put(仅限 raw kv 集群)duration,确认确实是 TiKV 写入慢了。常见的几种情况如下:

- append log 慢。TiKV Grafana 的 Raft I/O 和 append log duration 比较高,通常情况下是由于写盘慢了,可以检查 RocksDB - raft 的 WAL Sync Duration max 值来确认,否则可能需要报 bug。
- raftstore 线程繁忙。TiKV grafana 的 Raft Propose/propose wait duration 明显高于 append log duration。请查看以下两点:

- `[raftstore]` 的 `store-pool-size` 配置是否过小(该值建议在[1,5] 之间,不建议太大)。
- 机器的 CPU 是不是不够了。

- apply log 慢。TiKV Grafana 的 Raft I/O 和 apply log duration 比较高,通常会伴随着 Raft Propose/apply wait duration 比较高。可能的情况如下:

- `[raftstore]` 的 `apply-pool-size` 配置过小(建议在 [1, 5] 之间,不建议太大),Thread CPU/apply cpu 比较高;
- 机器的 CPU 资源不够了。
- Region 写入热点问题,单个 apply 线程 CPU 使用率比较高(通过修改 Grafana 表达式,加上 by (instance, name) 来看各个线程的 cpu 使用情况),暂时对于单个 Region 的热点写入没有很好的方式,最近在优化该场景。
- 写 RocksDB 比较慢,RocksDB kv/max write duration 比较高(单个 Raft log 可能包含很多个 kv,写 RocksDB 的时候会把 128 个 kv 放在一个 write batch 写入到 RocksDB,所以一次 apply log 可能涉及到多次 RocksDB 的 write)。
- 其他情况,需要报 bug。

- raft commit log 慢。TiKV Grafana 的 Raft I/O 和 commit log duration 比较高(4.x 版本的 Grafana 才有该 metric)。每个 Region 对应一个独立的 Raft group,Raft 本身是有流控机制的,类似 TCP 的滑动窗口机制,通过参数 [raftstore] raft-max-inflight-msgs = 256 来控制滑动窗口的大小,如果有热点写入并且 commit log duration 比较高可以适度调大改参数,比如 1024。

### 从 log 定位 I/O 问题

客户端报 `server is busy` 错误,特别是 `raftstore is busy` 会和 I/O 有相关性。通过查看监控:grafana -> TiKV -> errors 监控确认具体 busy 原因。`server is busy` 是 TiKV 自身的流控机制,TiKV 通过这种方式告知 `tidb/ti-client` 当前 TiKV 的压力过大,过一会儿再尝试。

TiKV RocksDB 日志出现 `write stall`:

`level0 sst` 太多导致 stall,添加参数 `[rocksdb] max-sub-compactI/Ons = 2(或者 3)` 加快 level0 sst 往下 compact 的速度,该参数的意思是将 从 level0 到 level1 的 compaction 任务最多切成 `max-sub-compactions` 个子任务交给多线程并发执行。

- `pending compaction bytes` 太多导致 stall,磁盘 I/O 能力在业务高峰跟不上写入,可以通过调大对应 cf 的 `soft-pending-compaction-bytes-limit` 和 `hard-pending-compaction-bytes-limit` 参数来缓解。

- 如果 pending compaction bytes 达到该阈值,RocksDB 会放慢写入速度。默认值 64GB,`[rocksdb.defaultcf] soft-pending-compaction-bytes-limit = "128GB"`

- 通常不太可能触发 RocksDB stop 写入,因为在达到 `soft-pending-compaction-bytes-limit` 的阈值之后会放慢写入速度。默认值 256GB,`hard-pending-compaction-bytes-limit = "512GB"`

- 如果磁盘 I/O 能力持续跟不上写入,建议扩容。如果磁盘的吞吐达到了上限(例如 SATA SSD 的吞吐相对 NVME SSD 会低很多)导致 write stall,但是 CPU 资源又比较充足,可以尝试采用压缩率更高的压缩算法来缓解磁盘的压力,用 CPU 资源换磁盘资源。

比如 default cf compaction 压力比较大,调整参数 `[rocksdb.defaultcf] compression-per-level = ["no", "no", "lz4", "lz4", "lz4", "zstd", "zstd"]` 改成 `compression-per-level = ["no", "no", "zstd", "zstd", "zstd", "zstd", "zstd"]`

### 从告警发现 I/O 问题

集群部署工具 (TiDB-Ansible, TiUP) 默认部署的告警组件,官方已经预置了相关的告警项目和阈值,I/O 相关项包括:

- TiKV_write_stall
- TiKV_raft_log_lag
- TiKV_async_request_snapshot_duration_seconds
- TiKV_async_request_write_duration_seconds
- TiKV_raft_append_log_duration_secs
- TiKV_raft_apply_log_duration_secs

## I/O 问题处理方案

1. 当确认为热点 I/O 问题的时候,需要参考[热点问题处理](/troubleshoot-hot-spot-issues.md)来消除相关的热点 I/O 情况。
2. 当确认整体 I/O 已经到达瓶颈的时候,且从业务侧能够判断 I/O 的能力会持续的跟不上,那么就可以利用分布式数据库的 scale 的能力,采用扩容 TiKV 节点数量的方案来获取更大的整体 I/O 吞吐量。
3. 调整上述说明中的一些参数,使用计算/内存资源来换取磁盘的存储资源。