# TiKV Performance Tuning

## Outline

-----------------

- TiKV Architecture

- Overall Resource Usage

- Tuning for Writes

- Tuning for Reads

- Profiling Tools

- Practice Exercises

## TiKV Architecture

-----------------

### TiKV Whole Picture

![Whole Picture](/hc/article_attachments/1500020267001/image-0.png)

### Layered Design

![Layered Design](/hc/article_attachments/1500020266981/image-1.png)

### TiKV Module

* Transactions use the Scheduler pool
* Raft uses the Raftstore pool
* RocksDB uses the Apply thread pool

![TiKV Module](/hc/article_attachments/1500019996282/image-2.png)

## Overall Resource Usage

----------------------

### CPU util

* TiKV is CPU bound for most workloads.
* Since TiKV is not using async IO (for now), 70%-80% CPU util per core usually means CPU is fully utilized.
* Grafana shows CPU usage as cpu\_util \* #cpu\_cores.

![CPU util](/hc/article_attachments/1500020267021/image-3.png)

~700% CPU usage per 8 vCPU TiKV nodes means TiKV is CPU bound

### CPU util of individual thread pools

* Even if the overall CPU is not fully utilized, individual thread pools may be fully utilized.
* Increase #threads in the specific thread pool to match your usage.

![CPU util of individual thread pools](/hc/article_attachments/1500019996322/image-4.png)

### Disk IO

* Check if disk latency/IOPS/throughput are high or have spikes.
* For cloud disk (AWS EBS, GCP storage disks, etc.), check if IOPS or throughput is hitting the limit.

* For some cloud vendors, there’s also IOPS/throughput limit per VM instance.

![Disk IO](/hc/article_attachments/1500019996302/image-5.png)

### TiKV Disk IO tasks

* The disk performance dashboard only shows average IO latency.
* Major TiKV IO tasks

* Foreground

* append log: raft dB WAL write / fsync.
* apply log: kvdb WAL write.
* storage read: kvdb get/scan.
* coprocessor read: kvdb scan.

* Background

* RocksDB flush/compaction
* Raftstore snapshots
* backup, data import, etc.

### RocksDB WAL duration

* raft dB and kvdb WAL write duration and fsync duration have a big impact on performance, especially for write-heavy workloads.

* Disk performance directly affects WAL write and fsync duration.
* fsync

* raft dB fsync after each write.
* kvdb fsync only on Raftstore admin command (e.g., raft log GC command)

![RocksDB WAL duration](/hc/article_attachments/1500020267061/image-6.png)

### TiKV Memory Usage

![TiKV Memory Usage](/hc/article_attachments/1500020267041/image-7.png)

### Cluster Memory

![Cluster Memory](/hc/article_attachments/1500019996342/image-8.png)

* TiKV memory allocations are all through Jemalloc.
* Jemalloc stats

* resident: approximation of memory used by Jemalloc.
* allocated: memory requested by TiKV.

* Difference between “resident” and “allocated” represents memory fragmentation.

![Allocated Stats](/hc/article_attachments/1500020267101/image-9.png)

### RocksDB block cache

* Shared by raft dB and kvdb.
* Check \`storage.block-cache.capacity\` config if block cache size is not as expected.
* Cache needs to warm up after TiKV starts.

* warmup is slow for write-only workload.

![RocksDB block cache](/hc/article_attachments/1500019996362/image-10.png)

Block cache sizes are the same under “RocksDB – kv” and “RocksDB – raft” panel, since raftdb and kvdb share block cache.

## Tuning for Writes

-----------------

### TiKV Write Flow

![TiKV Write Flow](/hc/article_attachments/1500019996382/image-11.png)

### gRPC duration

* Process time spent in TiKV (excluding network latency)

![gRPC duration](/hc/article_attachments/1500019996402/image-12.png)

### TiKV Scheduler Pool

![TiKV Scheduler Pool](/hc/article_attachments/1500020267161/image-13.png)

### scheduler worker pool

* Check if the scheduler worker pool is almost full?

* Increase scheduler-worker-pool-size.

![scheduler worker pool](/hc/article_attachments/1500020267121/image-14.png)

### Storage Async Write Duration    

* Is it slow in Raftstore?

![Storage Async Write Duration](/hc/article_attachments/1500020267141/image-15.png)

### TiKV Raftstore Pool

![TiKV Raftstore Pool](/hc/article_attachments/1500019996442/image-16.png)

### Raftstore Apply Pool and Append Pool

* Are they balanced?

* Are raftstore CPUs balanced between TiKV instances?
* Are apply CPUs balanced between TiKV instances?

* Is the raftstore pool or apply pool almost full? Increase if needed.

* raftstore.store-pool-size (default: 2)
* raftstore.apply-pool-size (default: 2)

* Increasing their size sometimes increases performance, even if they are not full.

* hard to reason about. Need some experimentation.

![Raftstore Apply Pool and Append Pool](/hc/article_attachments/1500020267181/image-17.jpeg)

### Enable Hibernate Regions

* Raftstore heartbeat can consume a lot of CPU

* Especially region number per node is huge (e.g. high data volume)

* Hibernate region

* Reduce raftstore heartbeat if thereâ€™s no traffic for a region
* Will be enabled by default from 5.0.2
* Consider enabling it if

* heartbeat message is more than append message in raftstore
* raftstore per thread CPU util > 80%

![Enable Hibernate Regions](/hc/article_attachments/1500020267221/image-18.png)

Check “Messages” in the “Raft message” panel to see if the heartbeat message is more than the appended message.

### Raftstore Append and Apply Duration

![Raftstore Append and Apply Duration](/hc/article_attachments/1500020267201/image-19.png)

### Raftstore flow control

* Are log commits slow?

* Make flow control more aggressive.

* raftstore.raft-max-inflight-msgs and raftstore.raft-max-size-per-msg

![Raftstore flow control](/hc/article_attachments/1500019996522/image-20.png)

### TiKV Write Flow to RocksDB

![TiKV Write Flow to RocksDB](/hc/article_attachments/1500019996482/image-21.png)

### RocksDB Write Duration

* Are RocksDB writes slow?

![RocksDB kv ad raft](/hc/article_attachments/1500019996462/image-22.jpeg)

![write duration](/hc/article_attachments/1500019996502/image-23.jpeg)

![write stall duration](/hc/article_attachments/1500020267241/image-24.jpeg)

* RocksDB write steps

* Enter and wait in write queue
* Preprocessing (switch memtable, etc.)
* Write WAL and fsync (disk IO)
* Write memtable
* Post Processing (update seqno)

* High WAL/sync duration means IO is slow

* Check disk metrics
* Check if RocksDB compaction flow is too high or too spiky

![RocksDB Graphs](/hc/article_attachments/1500020267281/image-25.png)

## Dealing with Write Stall

* Write stall means compaction is not able catch up
* Identify stall reason from metrics
* Identify resource limit

* reduce CPU consumption

* change compression level

* rocksdb.\[cf\].compression-per-level

* reduce disk bandwidth consumption

* consider enabling Titan

* For temporary write stall, increase stall trigger

* rocksdb.level0-slowdown-writes-trigger
* rocksdb.level0-stop-writes-trigger
* rocksdb.soft-pending-compaction-bytes-limit
* rocksdb.hard-pending-compaction-bytes-limit

![Write Stall Reason](/hc/article_attachments/1500019996542/image-26.png)![RocksDB CPU](/hc/article_attachments/1500020267261/image-27.png)![Compaction flow](/hc/article_attachments/1500019996562/image-28.png)

### Throttling Background IO

![Compaction Flow](/hc/article_attachments/1500019996562/image-28.png)

* Reduce impact of background IO (mainly RocksDB compaction)
* RocksDB rate limiter

* Can only limit compaction IO
* rocksdb.rate-bytes-per-sec = “100MB” (throttle compaction to 100MB/s)
* auto-tuned rate limit to smooth compaction flow (since 5.0.0)

* TiKV rate limiter (will be available in 5.1.0)

* Can limit total IO (including Raftstore snapshot, data import/export IO, etc.)
* storage.io-rate-limit.max-bytes-per-sec = “100MB”

### Titan    

![Titan architecture](/hc/article_attachments/1500020267341/image-29.png)

* Separate large value from RocksDB

* The large value stored in Titan does not participate in RocksDB compaction.

* Reduce disk throughput consumption by compaction.

* Stale values are cleaned up by Titan GC.

* Consider enabling Titan if …

* Workload writes a lot of large values (> 1K), and
* Disk throughput is bottleneck, and
* No scan or do not care about scan performance.

* Enabling Titan

* rocksdb.titan.enabled = true
* Has some more tunables.

## Tuning For Reads

----------------

### TiKV Read Flow

![TiKV Read Flow](/hc/article_attachments/1500020267301/image-30.png)

### Read Thread Pools

* Unified thread pool

* Implements multilevel feedback queue
* Adaptive thread number
* Fairness between small and large tasks

* Storage read pool / coprocessor read pool

* Simple thread pool with priority

![Unified read pool CPU](/hc/article_attachments/1500020267321/image-31.png)![Storage ReadPool CPU](/hc/article_attachments/1500020267381/image-32.png)![Coprocessor CPU](/hc/article_attachments/1500020267401/image-33.png)

Read thread pools (2)

<table><tbody><tr><td colspan="1" rowspan="1"><p class="wysiwyg-color-black wysiwyg-font-size-medium wysiwyg-text-align-left"><span class="wysiwyg-color-black wysiwyg-font-size-medium">Default value</span></p></td><td colspan="1" rowspan="1"><p class="wysiwyg-color-black wysiwyg-font-size-medium wysiwyg-text-align-left"><span class="wysiwyg-color-black wysiwyg-font-size-medium">Storage Read</span></p></td><td colspan="1" rowspan="1"><p class="wysiwyg-color-black wysiwyg-font-size-medium wysiwyg-text-align-left"><span class="wysiwyg-color-black wysiwyg-font-size-medium">Coprocessor Read</span></p></td></tr><tr><td colspan="1" rowspan="1"><p class="wysiwyg-color-black wysiwyg-font-size-medium wysiwyg-text-align-left"><span class="wysiwyg-color-black wysiwyg-font-size-medium">Unified Thread Pool</span></p></td><td colspan="1" rowspan="1"><p class="wysiwyg-color-black wysiwyg-font-size-medium wysiwyg-text-align-left"><span class="wysiwyg-color-black wysiwyg-font-size-medium">5.0, if no config specified</span></p></td><td colspan="1" rowspan="1"><p class="wysiwyg-color-black wysiwyg-font-size-medium wysiwyg-text-align-left"><span class="wysiwyg-color-black wysiwyg-font-size-medium">4.0 / 5.0, if no config specified</span></p></td></tr><tr><td colspan="1" rowspan="1"><p class="wysiwyg-color-black wysiwyg-font-size-medium wysiwyg-text-align-left"><span class="wysiwyg-color-black wysiwyg-font-size-medium">Copr Read Pool/ Storage Read Pool</span></p></td><td colspan="1" rowspan="1"><p class="wysiwyg-color-black wysiwyg-font-size-medium wysiwyg-text-align-left"><span class="wysiwyg-color-black wysiwyg-font-size-medium">&lt;= 4.0, or have config specified</span></p></td><td colspan="1" rowspan="1"><p class="wysiwyg-color-black wysiwyg-font-size-medium wysiwyg-text-align-left"><span class="wysiwyg-color-black wysiwyg-font-size-medium">&lt;= 3.0, or have config specified</span></p></td></tr></tbody></table>

* The unified thread pool is enabled by default only when no other configuration is specified for “readpool.storage” and “readpool.coprocessor” respectively
* Config

* readpool.stoarge.use-unified-pool = true
* readpool.coprocessor.use-unified-pool = true

### Adjust Thread Pool Size

* Is the thread pool almost full?

* Increase readpool.unified.max-thread-count

![Thread CPU](/hc/article_attachments/1500020267361/image-34.jpeg)![Unified read pool CPU](/hc/article_attachments/1500020267321/image-31.png)

### Coprocessor Wait Duration

* Is the wait duration too high?

* Too many large scans
* Imbalance
* Thread pool is too small.
* Wrong query plan

![Wait Duration](/hc/article_attachments/1500020267421/image-35.png)

### Block Cache

* Block cache hit rate

* low filter / index hit rate (<95%) indicates block cache too small
* data hit rate depend on block cache size and data locality

* Block cache flow

* total\_written: approximation of disk read IO size (plus page cache hits)
* filter\_insert / index\_insert / data\_insert: breakdowns

* Block cache size

* storage.block-cache.capacity

* Increase block cache if

* filter/index hit rate is low.
* Too much CPU time spent on data decompression.

* Decrease block cache if…

* IO read latency is high (counter intuitive)
* As long as filter/index hit rate keeps high, and more CPU usage is fine

* Hard to reason about. Need some experimentation.

![RocksDB KV](/hc/article_attachments/1500020267441/image-36.jpeg)![Block cache flow](/hc/article_attachments/1500019996582/image-37.png)![Block cache hits](/hc/article_attachments/1500019996602/image-38.png)

## Profiling Tools

---------------

### CPU Profiling    

* Profiling endpoint

* `http://<tikv\_server>:<status\_port>/debug/pprof/profile?seconds=60`
* curl the endpoint to obtain the file, then open in browser

![Flame Graph](/hc/article_attachments/1500020267461/image-39.png)

### Memory profiling

![Memory profiling](/hc/article_attachments/1500019996622/image-40.png)

* Profiling endpoint

* `http://<tikv\_server>:<status\_port>/debug/pprof/heap?seconds=60`

* Cluster deployed with TiUP

* update <deploy\_dir>/tikv-<port>/scripts/run\_tikv.sh to include

* export MALLOC\_CONF="prof:true,prof\_active:true,prof.active:true"

* Generating call-graph

* jeprof --pdf <tikv\_binary\_file> <profile\_file>

## Practice Exercises

------------------

### Scenario 1    

* TPCC workload
* Hardware: r5b.2xlarge (8 vCPU), EBS gp3 625MB/s, 6K IOPS
* Grafana: [TiKV](https://drive.google.com/file/d/1PPoVhQSzhm354CAngHrzZ2LHIuoeMHyt/view?usp=sharing), [Disk](https://drive.google.com/file/d/16bUQ23pAKK_mqclMg-yqTr-9JEde3apq/view?usp=sharing)

* Where is the bottleneck?

### Scenario 2    

* Customer workload
* Suspicious QPS drop around 14:16
* Grafana: [TiKV](https://drive.google.com/file/d/1PPoVhQSzhm354CAngHrzZ2LHIuoeMHyt/view?usp=sharing), [Disk](https://drive.google.com/file/d/16bUQ23pAKK_mqclMg-yqTr-9JEde3apq/view?usp=sharing)

* Where is the bottleneck?