tikv oom排查過程

賀子_DBA時代發表於2022-08-07

一、問題描述:

tikv例項OOM,並且所有的tikv例項記憶體使用率非常高,超過90%天天報警;引數都是預設的!,並且在看grafana可以看到tikv節點有oom現象!因為tikv oom後很快自動就重啟了(因為tikv服務配置中設定:Restart = always 是指程式或服務意外故障的時候可以自動重啟的模式。),所以報警沒有報出來!

二、具體我的排查過程

1、檢視Block cache 配置是否不合理

2、Raftstore Apply 速度跟不上 Raft Log 接收速度

3、coprocessor 響應過大引起的 OOM

4、網路問題或 gRPC 執行緒被打滿導致 Coprocessor 響應堆積

5、協程 (Coroutine) 洩漏導致 OOM

6、需要 Apply 的日誌過多導致 OOM

三、具體排查過程

1、檢視Block cache 配置是否不合理

首先說下RocksDB 的記憶體佔用:

為了提高讀取效能以及減少對磁碟的讀取,RocksDB 將儲存在磁碟上的檔案都按照一定大小切分成 block(預設是 64KB),讀取 block 時先去記憶體中的 BlockCache 中檢視該塊資料是否存在,存在的話則可以直接從記憶體中讀取而不必訪問磁碟。

BlockCache 按照 LRU 演算法淘汰低頻訪問的資料,TiKV 預設將系統總記憶體大小的 45% 用於 BlockCache,使用者也可以自行修改storage.block-cache.capacity 配置設定為合適的值,但是不建議超過系統總記憶體的 60%。

寫入 RocksDB 中的資料會寫入 MemTable,當一個 MemTable 的大小超過 128MB 時,會切換到一個新的 MemTable 來提供寫入。TiKV 中一共有 2 個 RocksDB 例項,合計 4 個 ColumnFamily(疑問不應該是六個嗎),每個 ColumnFamily 的單個 MemTable 大小限制是 128MB,最多允許 5 個 MemTable 存在,否則會阻塞前臺寫入,因此這部分佔用的記憶體最多為 4 x 5 x 128MB = 2.5GB。這部分佔用記憶體較少,不建議使用者自行更改。

storage.block-cache.shared:是否開啟共享 block cache。預設值:true,RocksDB 多個 CF 之間共享 block cache 的配置選項。當開啟時,為每個 CF 單獨配置的 block cache 將無效。

storage.block-cache.capacity :共享 block cache 的大小。預設值:系統總記憶體大小的 45%,單位:KB|MB|GB

storage CF (all RocksDB column families) 記憶體自適應,配置 storage.block-cache.capacity 引數即可實現 CF 之間自動平衡記憶體使用。

storage.block-cache 預設開啟 CF 自適應,無需修改。----rocksdb中的各個CF共享storage.block-cache.capacity設定的大小

storage.block-cache.shared: true

計算公式如下:

storage.block-cache.capacity = (MEM_TOTAL * 0.5 / TiKV 例項數量)

原理 預設情況下 TiKV 的 Block cache 會佔用伺服器上 45% 的記憶體,是否存在配置不合理,或節點上存在多個 TiKV 時,可能會由於 Block cache 過大而導致 OOM。

問題診斷和處理 檢查各個節點 Block cache 大小的配置,單個節點上所有 TiKV Block cache 總和不宜超過 45% 的總記憶體

伺服器記憶體是32G, 我們配置的是預設值,32*45%=14.4G,說明配置的是合理的;

具體如下所示:

mysql> show config where type='tikv' and name like 'storage.block-cache.capacity';

+------+---------------------+------------------------------+-------+

| Type | Instance | Name | Value |

+------+---------------------+------------------------------+-------+

| tikv | 10.100.40.117:20160 | storage.block-cache.capacity | 14.4GiB |

| tikv | 10.100.40.116:20160 | storage.block-cache.capacity | 14.4GiB |

| tikv | 10.100.40.115:20160 | storage.block-cache.capacity | 14.4GiB |

| tikv | 10.100.40.113:20160 | storage.block-cache.capacity | 14.4GiB |

| tikv | 10.100.40.118:20160 | storage.block-cache.capacity | 14.4GiB |

| tikv | 10.100.40.114:20160 | storage.block-cache.capacity | 14.4GiB |

+------+---------------------+------------------------------+-------+

6 rows in set (0.02 sec)

這裡需要注意如果你配置了tikv如下限制的話,那麼此時tikv的storage.block-cache.capacity=25*45%=11.2G

global:

resource_control:

memory_limit: 25G

還需要注意這個配置只能設定到 global 或者具體的tikv_servers節點上,不能設定到server_configs中tikv模組---感覺有點彆扭,因為如果你有1000個tikv需要設定這個memory_limit限制的時候,並且只想限制tikv節點的記憶體使用,這個時候你就不能設定global了,如果不能設定在server_configs中tikv模組,那就只能在各個tikv節點來執行了?那也太麻煩了!

小結:我設定的storage.block-cache.capacity引數是合理;

2、Raftstore Apply 速度跟不上 Raft Log 接收速度

如下圖所示,tidb 資料寫入的流程

committed 的 raft log 會由 store 執行緒透過一個 unbounded channel,如果 apply 卡住(可能由於 write stall 等等問題),channel 可能會佔用大量記憶體

問題診斷和處理 首先透過 By instance 的監控 TiKV-Details -> Rocksdb-KV -> Stall reasons 確認是否存在 write stall:

如果有 write stall,可根據監控上的 stall 原因來調節相關 RocksDB 引數

如果沒有 write stall,則考慮增加 apply 執行緒數,加快其消費 Raft Log 的速度

具體檢視,沒有write stall發生,如果cpu沒有瓶頸,可以增加 apply 執行緒數,具體是raftstore.apply-pool-size的tikv配置檔案引數,

小結:透過檢視TiKV-Details ->Thread-CPU 下的 Raft store CPU,參考值:低於 raftstore.store-pool-size * 85%, 因為我的當時的raftstore.store-pool-size為預設值2,所以Raft store CPU應該小於2*85%=170%, 如下圖所示符合這個規則,所以該引數可以不用調整! 也就是說明不是Raftstore Apply 速度跟不上 Raft Log 接收速度!

3、coprocessor 響應過大引起的 OOM

原理 TiKV 中 gRPC 內部的快取沒有大小限制,某些讀取資料量比較大的 coprocessor 請求(比如掃表請求),可能會由於結果太大導致佔用大量記憶體

問題診斷和處理 通常是由於大 region 或聚合查詢的中間結果太大所導致:

快速的處理方法是先透過 TiKV 上的 coprocessor slow-query 日誌(關鍵字 slow-query)找到讀取資料量大、處理時間長的 query 並 kill 掉;

可透過 TiKV-Details -> Server -> Approximate Region size 或 pd 的 region 資訊觀察是否有大 region,暫停其上的 query 並使用 pd-ctl 強制分裂大 region;

可調節 TiKV 的 server.grpc-memory-pool-quota 引數來限制 gRPC 使用的總記憶體大小(超過這個大小可能會導致卡頓)

4、網路問題或 gRPC 執行緒被打滿導致 Coprocessor 響應堆積

原理 在 TiKV 中 gRPC 內部的快取沒有大小限制,如果出現生成速度大於傳送速度的情況,或者網路存在問題導致發包堆積,從而導致 OOM;Coprocessor接受大量大查詢,返回的資料量很大,gRPC的傳送速度跟不上Coprocessor往外輸出的速度,導致資料堆積在tikv裡面,記憶體溢位

問題診斷和處理:

1)判斷gRPC的傳送速度跟不上Coprocessor往外輸出的速度;

TiKV Detail -> Coprocessor Overview -> Total Response Size ,該監控表示 所有 tikv 上 coprocessor 向上層返回 response 的大小。Node_exporter -> Network -> Network IN/OUT Traffice -> Outbound 指標,使用該監控檢查每個 tikv 節點的出站流量。如果所有 TiKV 的出站流量之和比 response 速度小很多則說明 coprocessor 資料有積壓。

如果是這個問題:

1、可以嘗試找到慢sql,最佳化sql;

2、同時可以嘗試增大server.grpc-concurrenc來提高grpc的併發度,同時可以考慮降低comprocessor的併發度(一般不建議)

readpool.coprocessor.high-concurrency

readpool.coprocessor.normal-concurrency

readpool.coprocessor.low-concurrency

3、可調節 TiKV 的 server.grpc-memory-pool-quota 引數來限制 gRPC 使用的總記憶體大小(超過這個大小會導致一些網路連線被強制中斷)

2)觀察 TiKV gRPC 執行緒是否出現瓶頸,考慮增加 gRPC 執行緒數;

TiKV Detail -> Thread CPU -> gRPC poll CPU,檢查 gRPC 執行緒是否繁忙。

gRPC poll CPU:gRPC 執行緒的 CPU 使用率,通常應低於 80% * server.grpc-concurrency,如果高於這個值,可以嘗試增到server.grpc-concurrenc的引數,

5、協程 (Coroutine) 洩漏導致 OOM

原理 如果出現由於硬體故障或者核心 bug 導致 TiKV 卡死,但網路服務還正常的情況下,協程可能會出現堆積,從而導致 OOM。

問題診斷和處理 該問題的復現機率非常低,按照現有監控不好排查。隻影響版本 v5.1.0,v5.1.1,v5.2.0,v5.2.1 問題已在 v5.1.2 & v5.2.2 修復。

6、需要 Apply 的日誌過多導致 OOM

原理 如果 TiKV Apply 速度過慢或者重啟導致堆積了大量的需要 Apply 的日誌時,就會導致 Log Entry 佔用大量的 Entry Cache 資源

問題診斷和處理 v5.1 之前的版本尚無法從現有監控資訊中判定根因。 v5.1 版本新增了針對 Entry Cache 使用情況的監控指標,可以透過 TiKV-Details->Server->Memory trace 來確認 Entry Cache 大小是否過高。

處理:可以調整下apply-pool-size的大小

三、最終處理辦法:

1)再global 這裡新增所有元件的最大使用記憶體數,重啟生效!注意修改該引數後,所有的元件都會被加到服務的配置檔案中了,例如tikv服務會寫到/etc/systemd/system/tikv-20160.service服務配置檔案中!

global:

resource_control:

memory_limit: 25G

2)配置tikv的grpc最大記憶體使用限制,提高grpc的執行緒數,提高apply raft的執行緒數,

server_configs:

tikv:

raftstore.apply-pool-size: 4

server.grpc-concurrency: 8

server.grpc-memory-pool-quota: 12884901888

關於引數server.grpc-concurrency

gRPC 執行緒池的大小預設配置(server.grpc-concurrency)是 5。由於 gRPC 執行緒池幾乎不會有多少計算開銷,它主要負責網路 IO、反序列化請求,因此該配置通常不需要調整。

如果部署的機器 CPU 核數特別少(小於等於 8),可以考慮將該配置(server.grpc-concurrency)設定為 2。

如果機器配置很高,並且 TiKV 承擔了非常大量的讀寫請求,觀察到 Grafana 上的監控 Thread CPU 的 gRPC poll CPU 的數值超過了 server.grpc-concurrency 大小的 80%,那麼可以考慮適當調大server.grpc-concurrency 以控制該執行緒池使用率在 80% 以下(即 Grafana 上的指標低於 80% * server.grpc-concurrency的值)

mysql> show config where type='tikv' and name like '%server.grpc-memory-pool-quota%';

限制每個元件的最大使用記憶體數!注意resource_control是寫近了tikv-20160.service這個服務配置檔案中了(重啟才能生效), 他不是寫進tikv配置檔案

vim /etc/systemd/system/tikv-20160.service [Unit] Description=tikv service After=syslog.target network.target remote-fs.target nss-lookup.target [Service] MemoryLimit=2G ###resource_control memory_limit: 2G 配置對應的引數 LimitNOFILE=1000000 LimitSTACK=10485760 User=tidb ExecStart=/data/tidb/tidb-deploy/tikv-20161/scripts/run_tikv.sh Restart=always RestartSec=15s [Install] WantedBy=multi-user.target

注意這個配置只能設定到 global 或者具體的tikv_servers節點上,不能設定到server_configs中tikv模組統一設定

global 配置示例:

global: user: "tidb" resource_control: memory_limit: "2G"

上述配置指定使用 tidb 使用者啟動叢集,同時限制每個元件執行時最多隻能使用 2GB 記憶體。

如果配置到了server_configs中tikv模組,在嘗試reload的時候報錯了,無法生成tikv的配置檔案,然後沒有重啟tikv節點

[root@B-JS25-BASE79-00 pd-2379]# tiup cluster reload tidb-test -R tikv

tiup is checking updates for component cluster ...

A new version of cluster is available:

The latest version: v1.10.2

Local installed version: v1.9.4

Update current component: tiup update cluster

Update all components: tiup update --all

Starting component `cluster`: /root/.tiup/components/cluster/v1.9.4/tiup-cluster /root/.tiup/components/cluster/v1.9.4/tiup-cluster reload tidb-test -R tikv

Will reload the cluster tidb-test with restart policy is true, nodes: , roles: tikv.

Do you want to continue? [y/N]:(default=N) y

+ [ Serial ] - SSHKeySet: privateKey=/root/.tiup/storage/cluster/clusters/tidb-test/ssh/id_rsa, publicKey=/root/.tiup/storage/cluster/clusters/tidb-test/ssh/id_rsa.pub

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [Parallel] - UserSSH: user=tidb, host=172.16.197.167

+ [ Serial ] - UpdateTopology: cluster=tidb-test

+ Refresh instance configs

- Generate config pd -> 172.16.197.167:2379 ... Done

- Generate config tikv -> 172.16.197.167:20160 ... Error

- Generate config tikv -> 172.16.197.167:20161 ... Error

- Generate config tikv -> 172.16.197.167:20162 ... Error

- Generate config tidb -> 172.16.197.167:4000 ... Done

- Generate config tidb -> 172.16.197.167:4001 ... Done

- Generate config tiflash -> 172.16.197.167:9000 ... Done

- Generate config prometheus -> 172.16.197.167:9090 ... Done

- Generate config grafana -> 172.16.197.167:3000 ... Done

Error: init config failed: 172.16.197.167:20162: executor.ssh.execute_failed: Failed to execute command over SSH for 'tidb@172.16.197.167:22' {ssh_stderr: unknown configuration options: resource_control

, ssh_stdout: , ssh_command: export; PATH=$PATH:/bin:/sbin:/usr/bin:/usr/sbin /data/tidb/tidb-deploy/tikv-20162/bin/tikv-server --config-check --config=/data/tidb/tidb-deploy/tikv-20162/conf/tikv.toml --pd "" --data-dir "/data/tidb/tidb-data/tikv-20162"}, cause: Process exited with status 1: check config failed

Verbose debug logs has been written to /root/.tiup/logs/tiup-cluster-debug-2022-07-14-13-23-01.log.

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29654823/viewspace-2909337/,如需轉載,請註明出處,否則將追究法律責任。

相關文章