ZabbixのValueCacheを正しく理解して、大規模な監視環境でも効率良く処理させよう

本記事は備忘のための記事です。なので細かい検証とかまで深くできているわけではないので参考までにとどめてください。
Zabbix3.0系での話です。(おそらく4.0とかでも似たような挙動だとは思います。)

----

ネット上の記事だと、「ValueCacheは危険」的なものをちらほらと見かけるので、改めてValueCacheの挙動について整理しておきます。
ValueCacheは大量の監視データの処理の際に、トリガー評価を非常に高速に行い、DBへの負担を激減させるために有効な仕組みなので、ぜひ仕様を理解して効果的に活用しましょう。

# ValueCacheとは

ValueCacheとは、Zabbixのトリガー条件を評価する際に活用されるキャッシュです。
Zabbixは監視結果を受信すると、一旦、HistoryCacheと呼ばれるメモリ領域に格納します。
そのHistoryCacheのデータをDBに同期する際、並行してトリガー評価が行われる仕組みとなります。
この時、トリガーの評価のためだけに都度DBからデータをSELECTすると非常にDBとのやり取りが増えることから、受信データをDBに書き込むと同時にValueCacheという共有メモリ領域にデータを書き出します。
こうすることで、トリガー評価の際、まずValueCache上(共有メモリ上)にあるデータを取得して評価することができ、DBにアクセスすることなく評価を完結させることができます。

ValueCacheはZabbixの2.2のバージョン以降で利用できるものです。
なお、トリガー評価はZabbix Serverで統一して行うので、Zabbix ProxyにはこのValueCacheの仕組みはありません。

# ValueCacheを使うことによる効果

ValueCacheを有効にするとトリガーの評価にかかる処理が劇的にスピードアップします。
トリガーの評価処理は、Zabbix Serverの各種プロセスの内、DB syncerプロセスが担当します。
DB syncerプロセスはHistoryCache内のデータをDBに書込処理を行うのと並行して、ValueCacheに値を書き出し、トリガーの条件式を評価、イベント(障害、正常)の発行、アクション情報を確認してエスカレーション情報を登録まで行います。
ValueCacheをうまく使えていないと、DB syncerのプロセスの各処理に時間がかかり、大量のヒストリデータを扱う際に処理が滞ることとなります。

## ValueCache有効時と無効時のパフォーマンスの違い

とあるログ監視のアイテムに突如数万件のデータがバースト的に監視されてしまった場合、ValueCache有効時と無効時でどれぐらい処理量に差が出るかの参考値です。
マシンの性能や監視結果のログデータサイズ、DBのチューニング度合い等により様々異なりますが、簡易な環境であまりチューニングもしていない状況だと以下のレベルで性能差が出てきます。

|ValueCache|1分間に処理できるヒストリデータ件数目安|
|:----------------:|:----------------------------------:|
|有効時|30,000件～40,000件程度|
|無効時|2,000件～3,000件程度|

# ValueCacheの内部構造

## ValueCacheの全体サイズ

ValueCacheはzabbix_server.confのValueCacheSizeで全体量を定義できます。
ここで設定したサイズの共有メモリがZabbix Serverの起動時に確保されることとなります。

* 0: ValueCacheの内部機能を利用しない
* 最小値: 128KB
* 最大値: 64GB

## ValueCacheへのデータ登録

ValueCacheにデータが登録されるのは主に以下の3つのタイミングです。

1. History Cacheに格納されているデータをDBに同期する時に格納
2. トリガーの評価時に評価に必要なデータがValueCache上になかった場合、DBから取得して格納
3. アクション実行時のマクロ展開で必要なデータがValueCache上になかった場合、DBから取得して格納

### 1. History Cacheに格納されているデータをDBに同期する時に格納

DB syncerがHistory Cache上のデータをDBに書き込む際、各監視結果の内、トリガー評価で利用する対象のものについて、データをValueCache上に書き込みます。1件のデータを処理する毎に都度ValueCacheに書き込みます。

### 2. トリガーの評価時に評価に必要なデータがValueCache上になかった場合、DBから取得して格納

トリガーの条件式に、過去のデータを活用した評価を行うような書き方になっている場合、評価を行う際に必要となる過去データがValueCache上に存在しなければ、DBから取得しValueCacheに登録します。

```e.g.
{ホスト名:アイテムキー.avg(#5)}>100
```

このような条件式の場合、過去5回分の監視結果の平均値を求めることになるので過去5回分のデータがValueCache上に登録されている必要があります。

### 3. アクション実行時のマクロ展開で必要なデータがValueCache上になかった場合、DBから取得して格納

基本的には上記の2.の挙動と同じです。マクロの中で過去のデータも含めて取得して展開するようなものがあると、こちらに必要な分のデータもValueCache上に保存します。

## ValueCacheからのデータ削除

ValueCache上のデータは、上記2.や3.の処理時に、ValueCacheからデータを取得したタイミングで不要な古いデータが削除されるようになっています。各アイテム毎に、トリガー評価やマクロ展開に必要となるデータの範囲がどれぐらいであるかの情報(「このアイテムは過去1時間分の情報が必要」等)を保持しています。
この範囲に従って、それ以上に古いデータを取得処理の際に削除します。この処理により、ValueCacheにデータが溜まり続けるのを防いでいます。

## Low memoryモード

上記のように追加処理や削除処理を繰り返しながら効率良くトリガー評価等を行っているのですが、場合によってはValueCacheが消費され枯渇するケースも発生します。ValueCacheから新規でメモリを確保しようとした際、空き容量不足で確保に失敗すると、ValueCacheのモードがNormalモードからLow memoryモードにスイッチします。
Low memoryモードになった場合、直近のデータ(直近1日以内にアクセスのあるものでヒット率の高いもの)はValueCache上に確保してトリガー評価処理のパフォーマンスの維持をしつつ、新規で入ってくるデータについてはValueCacheに登録を行わないような挙動になります。

新規で値がValueCacheに格納されなくなる=評価の都度DBへの問合せが発生することになります。
そうなると、ValueCacheを無効にした時のようにパフォーマンスが低下します。
Low memoryモードからの復帰には、丸1日経過するか、Zabbix Serverを再起動させなければいけません。

一応内部の処理では、Low memoryモードになる直前に、一旦空き容量を確保するため、直近1日以上アクセスのないキャッシュは解放するようですが、根本的にアイテム数が多かったり、トリガー評価式で大量の監視データを使うケースでは、ValueCacheSizeが小さいとLow memoryモードになってしまうので、ValueCacheの最大サイズのチューニングが必要です。

## ログ監視のバースト時の注意

また、ログ監視を行っていて、突如大量のログ検知が発生した場合には注意です。
Zabbixの内部プロセスでは、トリガー評価時に不整合が発生しないよう、現在評価中のValueCacheのデータには「保留」のステータスを割り振るようです。
この場合、ValueCache上からのデータ削除処理がパスされ、データが残り続けるようです。
また、ログ監視でバーストが発生すると、同一のトリガーに対してログの件数分評価が大量発生することになります。
そのため、長時間に渡って1つのDB Syncerプロセスが該当のトリガーをロックして他のDB syncerプロセスで処理させないようにするため、DB syncerの処理をスケールさせることもできない状態に陥ります。
シーケンシャルにバーストした各ログを処理し続け、その間ValueCacheにログデータが書き込まれ続けることになるため、ValueCacheの枯渇を招きやすい状況になってしまいます。
もしこういった状況が発生し得るのであれば、最悪のケースを想定したValueCacheのサイズ見積が必要となります。 

# ValueCacheの状態を監視

実際に運用を始めた後、ValueCacheがどのような状態で稼働しているのかを監視するには、Zabbixの内部監視の機能が活用できます。

|アイテムキー|監視内容|
|:----------|:------|
|zabbix[vcache,cache,hits]|ValueCacheのヒット数|
|zabbix[vcache,cache,misses]|ValueCacheの非ヒット数|
|zabbix[vcache,cache,requests]|ValueCacheへのリクエスト数|
|zabbix[vcache,cache,mode]|ValueCacheのモード(0:Normal,1:Low memory)|
|zabbix[vcache,buffer,free]|ValueCacheの空きサイズ|
|zabbix[vcache,buffer,pfree]|ValueCacheの空き率|
|zabbix[vcache,buffer,used]|ValueCacheの使用中サイズ|
|zabbix[vcache,buffer,pused]|ValueCacheの使用率|
|zabbix[vcache,buffer,total]|ValueCacheの全体サイズ|

この辺りのデータを見つつ、DB syncerプロセスの状況を見つつ状況を眺めると状態がわかります。

# まとめ

文章ばかりの記事になりましたが、ValueCacheの紹介でした。中小規模で特に普通に監視をしていればデフォルトの設定のままでもValueCacheは特に問題にならないかと思います。ですが、環境が大規模だったり、監視データが高頻度で多量化してくると、気にかけた方が良い点かと思います。