こんにちは!今日もシステムの裏側を支える運用監視の業務、本当にお疲れ様です。
Zabbixを導入してしばらく経ち、監視対象(ホストやアイテム)が増えてくると、ほぼ100%のエンジニアがぶつかる「巨大な壁」があります。そう、「データベース(DB)の急速な肥大化と、それに伴うDisk I/Oの高騰・性能劣化」です。
「ディスク容量アラートが止まらない…」
「Zabbixの画面表示が重くて開かない…」
「Housekeeperプロセスが100%に張り付いてDBを圧迫している…」
そんな悲鳴を何度も耳にしてきました。ですが、安心してくださいね。この記事で解説する「Housekeeperのチューニング」と「テーブルパーティショニング」の概念・実装手順をマスターすれば、DB容量の悩みは嘘のように消えてなくなります。
毎日DB容量に怯える運用から脱却し、何億件のメトリクスが飛び交おうとも涼しい顔で動く「超高速・堅牢なZabbix環境」を一緒に構築していきましょう!
—
1. Zabbix DB肥大化の真相と「Housekeeper」の限界
まずは「なぜDBが圧迫されるのか」「なぜデフォルトの設定では限界が来るのか」という仕組み(構造)からわかりやすく紐解いていきましょう。
Zabbixが保持する2つの主要データ
Zabbixのデータベースを圧迫している犯人の99%は、以下の2種類のテーブル群です。
| データ種別 | テーブル例 | 内容 | データの増え方 |
| :— | :— | :— | :— |
| ヒストリ(History) | `history`, `history_uint`, `history_str` など | 収集した「生の測定値」(例: 1分ごとのCPU使用率) | 数秒〜数分おきに超大量に蓄積される |
| トレンド(Trends) | `trends`, `trends_uint` | 1時間ごとの「統計値」(最大・最小・平均値) | ヒストリより圧倒的に少ないが長期保存される |
特に`history_uint`(数値・整数)と`history`(数値・浮動小数点)は、大規模環境になると1日で数千万〜数十億行のレコードが追加されます。
標準機能「Housekeeper(ハウスキーパー)」の限界
Zabbixには、設定した保存期間(例: 7日間)を過ぎた古いデータを自動削除する「Housekeeper」という内部プロセスが備わっています。
一見便利な機能に見えますが、DBの内部では何が起きているでしょうか?
Housekeeperは、定期的に以下のようなSQLを発行しています。
— Housekeeperが裏で実行している処理(イメージ)
DELETE FROM history_uint WHERE clock < 1700000000 LIMIT 5000;
ここが最大の罠です。データベースにおいて大量の行を `DELETE` する処理は超高負荷なのです。
1. 大量のログ書き込み: `DELETE` はトランザクションログ(WALやRedoログ)を大量に生成します。
2. インデックスの断片化(虫食い状態): データが削除されても、ディスク上のファイルサイズは小さくなりません。スカスカの無駄な領域(空き領域)が残るだけです。
3. I/Oのバースト: 削除処理と検索・書き込み処理がバッティングし、ディスクI/Oが枯渇してZabbix全体の動きが停止します。
つまり、標準のHousekeeperに巨大なヒストリデータの掃除を任せるのは、「巨大な部屋のゴミを、ピンセットで1個ずつ拾って捨てている」ようなものなのです。
救世主:「テーブルパーティショニング」とは?
この問題を根本解決するのが「テーブルパーティショニング」です。
これは、1つの巨大なテーブル(例: `history_uint`)を、日付ごと(1日単位や1ヶ月単位)の「小さな小部屋(パーティション)」に内部的に分割して管理する技術です。
【従来のテーブル】
history_uint (10億行の巨大な1ファイル) ➔ DELETE処理でDBが悲鳴を上げる!
【パーティショニング後】
history_uint
├── history_uint_p2024_10_24 (10月24日の小部屋)
├── history_uint_p2024_10_25 (10月25日の小部屋)
└── history_uint_p2024_10_26 (10月26日の小部屋)
古くなったデータを消したい時は、ピンセットで消す(`DELETE`)のではなく、「部屋ごとゴミ箱に投げ捨てる(`DROP TABLE`)」を行います。
`DROP TABLE` はDBにとって一瞬で終わる処理です。ログもほとんど吐かず、インデックスも綺麗に消え去り、ディスク容量も即座にOSへ返還されます。まさに魔法のようなソリューションですね!
—
2. ステップ1:Zabbix設定でHousekeeperをオフにする
パーティショニングを導入する前に、まずはZabbix標準のHousekeeperが「ヒストリ」と「トレンド」を切り刻もうとする動きを止めましょう。
設定手順(Zabbix Web UI)
1. Zabbixの管理画面にログインします。
2. 【管理】 ➔ 【一般設定】 ➔ 【ハウスキーピング】 を開きます。
3. 「ヒストリ」 および 「トレンド」 の枠内にある 「内部ハウスキーパー有効」のチェックを外します。
(※イベントやアラート、監査ログなどの軽量なデータは、ハウスキーパーを有効のままにしてOKです)

zabbix_server.conf の最適化
Zabbixサーバーの設定ファイル(`/etc/zabbix/zabbix_server.conf`)を開き、無駄なHousekeeperプロセスが起動しないようにチューニングします。
/etc/zabbix/zabbix_server.conf
Housekeeperの実行間隔(デフォルトは1時間)
ヒストリ/トレンドをDB側で削除するため、頻度を減らすか、無効化の調整を行います
HousekeepingFrequency=1
1回の実行で削除する最大件数(負担を減らすための制限)
MaxHousekeeperDelete=5000
設定を変更したら、Zabbixサーバーを再起動しておきましょう。
sudo systemctl restart zabbix-server
—
3. ステップ2:実践!テーブルパーティショニングの実装
ここからは、実際のデータベース環境に合わせた実装手順を解説します。
ご自身の環境(PostgreSQL か MySQL/MariaDB)に合わせて読み進めてくださいね。
—
パターンA:PostgreSQL (TimescaleDB) の場合【最も推奨!】
Zabbix公式も強力に推進しているのが、PostgreSQLの時系列データベース拡張機能である「TimescaleDB」を利用する方法です。これを使えば、面倒なパーティション管理のスクリプトを自分で書く必要がなくなります。
1. TimescaleDB拡張の有効化(SQL)
PostgreSQLに管理者ユーザー(`postgres`)でログインし、Zabbixデータベースに対してTimescaleDBを有効化します。
— Zabbixデータベースに接続
\c zabbix
— TimescaleDB拡張機能を有効化
CREATE EXTENSION IF NOT EXISTS timescaledb CASCADE;
2. Hypertables(ハイパーテーブル)化
Zabbixが提供している公式スキーマ変換スクリプト(`timescaledb.sql`)を実行するか、手動で`create_hypertable`を発行して、ヒストリ・トレンドテーブルを時系列テーブルに変換します。
— 例: history_uint テーブルを1日単位(86400秒)のパーティションに変換
SELECT create_hypertable(‘history_uint’, ‘clock’, chunk_time_interval => 86400, migrate_data => true);
— 例: trends_uint テーブルを30日単位(2592000秒)のパーティションに変換
SELECT create_hypertable(‘trends_uint’, ‘clock’, chunk_time_interval => 2592000, migrate_data => true);
3. 自動クリーンアップ(Retention Policy)の設定
TimescaleDBには、指定期間を過ぎたパーティション(Chunk)を自動削除する機能が標準で備わっています。これだけでHousekeeperの代わりが完了します!
— ヒストリデータ(生のデータ)を7日(604800秒)で自動削除するポリシーを追加
SELECT add_retention_policy(‘history_uint’, INTERVAL ‘7 days’);
— トレンドデータ(統計データ)を365日で自動削除するポリシーを追加
SELECT add_retention_policy(‘trends_uint’, INTERVAL ‘365 days’);
—
パターンB:MySQL / MariaDB の場合(レンジパーティショニング)
MySQL/MariaDBの場合は、UNIXタイムスタンプ(`clock`列)を利用したレンジパーティショニングを構築し、日々のパーティション作成・削除をストアドプロシージャとCronで自動化するのが王道です。
1. テーブルのパーティション化(例: `history_uint`)
※注意: 既存の大きなテーブルを改変する場合、メンテナンス時間を確保して実行してください。
— MySQLにログイン後、Zabbix DBを選択
USE zabbix;
— history_uint テーブルを日付(UNIX Time)でレンジ分割する変更(例)
— ※プライマリキー構造の調整が必要な場合があります
ALTER TABLE history_uint PARTITION BY RANGE (clock) (
— 過去のデータ用のデフォルトパーティション
PARTITION p2024_10_24 VALUES LESS THAN (UNIX_TIMESTAMP(‘2024-10-25 00:00:00’)),
PARTITION p2024_10_25 VALUES LESS THAN (UNIX_TIMESTAMP(‘2024-10-26 00:00:00’)),
PARTITION p2024_10_26 VALUES LESS THAN (UNIX_TIMESTAMP(‘2024-10-27 00:00:00’))
);
2. パーティション管理スクリプト(Bash + SQL)の作成
「未来のパーティションを毎日1つ作成し、古いパーティションを1つ破棄する」シェルスクリプトを作成し、OSのCronで回します。
以下は、そのまま使える実用的で安全な自動化スクリプトの例です。
!/bin/bash
==============================================================================
Zabbix MySQL Partition Management Script
目的: 未来のパーティション自動作成と古いパーティションのDROP
==============================================================================
DB_USER=”zabbix”
DB_PASS=”YourSecurePassword” # ※実際のパスワードに置き換えてください
DB_NAME=”zabbix”
保持期間の設定
HISTORY_RETENTION_DAYS=7
今日の日付と削除対象の日付を計算
TARGET_DROP_DATE=$(date -d “${HISTORY_RETENTION_DAYS} days ago” +%Y_%m_%d)
TARGET_DROP_PARTITION=”p${TARGET_DROP_DATE}”
FUTURE_CREATE_DATE=$(date -d “2 days” +%Y-%m-%d)
FUTURE_PARTITION_NAME=”p$(date -d “2 days” +%Y_%m_%d)”
FUTURE_TIMESTAMP=$(date -d “${FUTURE_CREATE_DATE} 00:00:00” +%s)
1. 未来のパーティションを追加 (例: history_uint)
echo “Creating future partition: ${FUTURE_PARTITION_NAME}…”
mysql -u”${DB_USER}” -p”${DB_PASS}” “${DB_NAME}” <
—
4. ステップ3:動作確認(HelloWorld的な確認手法)
設定が完了したら、「本当にパーティションが作られて、データが分散して格納されているか?」を確認してみましょう。
ここをしっかり確認するのが、プロのエンジニアの嗜みです!
確認方法1:パーティション一覧の確認(MySQL例)
以下のSQLを実行し、テーブルが綺麗な小部屋に分かれているか確認します。
SELECT
TABLE_NAME,
PARTITION_NAME,
TABLE_ROWS,
AVG_ROW_LENGTH,
DATA_LENGTH
FROM
information_schema.PARTITIONS
WHERE
TABLE_SCHEMA = ‘zabbix’ AND TABLE_NAME = ‘history_uint’;
【期待される結果】
`PARTITION_NAME` に `p2024_10_25`, `p2024_10_26` のような名前がズラリと並び、各行数(`TABLE_ROWS`)がそれぞれの部屋に分散されていれば大成功です!
確認方法2:データ検索の高速化(EXPLAIN構文)
実際にデータを取りに行く際、DBが無駄な領域を無視して「特定のパーティションだけ」を見に行っているか(Partition Pruning)を確認します。
EXPLAIN SELECT FROM history_uint WHERE clock >= 1729810800 AND clock <= 1729814400; 実行結果の `partitions` 列に、全体ではなく `p2024_10_25` などの特定のパーティション名だけが表示されていれば、検索パフォーマンスも爆発的に向上している証拠です。
—
5. まとめ:毎日の運用負荷を「ゼロ」にするために
最後に、今回の重要なポイントを振り返りましょう。
1. DB肥大化の犯人: `history_` テーブルへの大量の書き込みと、標準Housekeeperの `DELETE` 負荷。
2. 解決策の要: Housekeeperのヒストリ削除を無効化し、テーブルパーティショニングを導入する。
3. 運用の劇的変化:
- ゴミ掃除が `DELETE`(激重)から `DROP PARTITION`(一瞬)へ変わる。
- インデックスの断片化が起きなくなり、ディスク容量が即座に解放される。
- Web画面のグラフ描画速度が見違えるほど高速化する。
最初は「データベースの構造を変更する」と聞くと少しハードルが高く感じるかもしれません。ですが、一度この仕組みを構築してしまえば、今後何年運用してもZabbixのDB容量に頭を悩ませることはなくなります。
これをマスターすれば、毎日の障害対応やキャパシティ管理の作業が劇的に楽になりますよ!
ぜひ安全な検証環境から試してみてくださいね。あなたのZabbix運用が、最高に快適なものになることを心から応援しています!