ZabbixのDB(MySQL)をパーティショニングする(2)


テーブルをパーティショニング

さて、いよいよテーブルをパーティション化します。

標準状態のテーブルのままだと、下図のような感じで、1つの領域に順次データが蓄積され、肥大化してしまうわけですが‥

Table01

前回の記事で触れたように、時系列を示す clock 値を利用してパーティショニングすることで、下図のように、テーブル内に仮想的な枠ができる感じになります。

Table02

とりあえずは、各テーブルをパーティション化するコマンドを流してしまいます。
最初のパーティション化段階では、clock 基準では全てを1つのパーティションへ入れるような形で指定し、それらを、itemid ベースで 10 個ずつに分けるように指定しておきます。
このようにすることで、後から clock で分けるルールを追加指定することで細分化させることができ、さらに、古くなったパーティションだけを DROP することもできるようになります。

時系列での分割 – プロシージャの作成

前項でとりあえずは itemid 基準で 10 分割するようなパーティション化を施したわけですが、時系列基準でのルールについては、マトモに指定していません。
このままでは、LESS THAN (MAXVALUE) に合致するデータ、すなわち全てのデータが、全部1つの扱いにしかならず、全然ありがたくない状態になってしまいます。

前回触れたように、数日単位で分けて管理したいのですが、パーティション操作のためには SQL を実行する必要があります。
しかも、数日単位で「次のデータを入れるためのパーティション」を準備、「古くなったパーティションを破棄」と、毎度毎度、手で実行するのは現実的じゃありません‥。
そこで、MySQL のストアドプロシージャ機能を使って、関連処理を自動化してしまいます。
利用するプロシージャを以下に示します。

少々長いコードを貼りつけてしまいました。これらは、大きく3つのプロシージャで構成されています。

  • partition_drop_older_or_empty
    古くなったパーティションを破棄 (DROP) するプロシージャ
  • partition_create_newer
    MAXVALUE のパーティションを、未来に備えて2分割するプロシージャ
  • maintenance
    上記2プロシージャを連続実行するメインの処理

いずれも、現在日時をベースに、予め決めておいた分割間隔・保存期間に従って、新しいパーティションの作成や、古いパーティションの破棄処理を行うように実装されています。

時系列での分割 – 詳細解説

おおよその処理の流れや、実行していることの意味合いについて、少し触れておきます。

maintenance

先頭付近、KEEPDAYS で、ヒストリ系データの保持期間を決めています。前回の記事で決めたとおり、45 日という設定にしてあります。
同様に、SPANDAYS で、分割の基準になる日数を指定しています。前回の記事で決めたとおり、3 日という設定にしています。
ここでの設定値を変えるだけで、保持期間や分割単位を変更できるようにしてありますが、運用を開始してしまってからは、大きく変更しないことをお薦めします‥。
途中で変えてしまった場合の検証は十分に実施していません(汗)

partition_drop_older_or_empty

DB 情報を保有している information_schema から、プロシージャの実行時に与えられたテーブル名に関するパーティション分割情報を調べています。
分割されている複数のパーティションのうち、KEEPDAYS を超えるデータのみが保存されているパーティション、すなわち、古いデータしか含まれておらず、もう捨ててしまっても良いパーティションを探します。

古いパーティションが見つかった場合には、DROP PARTITION 命令を使って、当該パーティションを DROP してしまいます。この命令は、DELETE ~ WHERE 命令と違って、瞬時で処理が戻るため、あまり DB の負荷になることはありません。

処理のついでに、存在しているパーティションのうち、データが全く含まれていないパーティションについても、削除処理をしています。
パーティション化だけで古いデータを消している場合には滅多に起こり得ない状態ですが、Zabbix の HouseKeeper 処理を併用したり、別の削除処理を利用しているなどの場合、ムダなパーティションを排除できるので、このようにしてあります。

partition_create_newer

older_or_empty と同じように、information_schema から、テーブルに関する情報を調べています。
現在時刻よりも未来のデータが入るように指定されたパーティションが、既に存在している場合には特になにもしません。
前回のパーティション分割実行から一定以上の時間が経過していて、前回作成した新しいパーティションに対し、既に何らかのデータが入りはじめているような場合に、本来の機能が動作します。
未来のデータが入るように指定されたパーティションが見当たらない場合、現在時刻よりも未来のデータ、SPANDAYS に指定された期間分のデータが入るように指定した新しいパーティションを作成します。

時系列での分割 – プロシージャの動作確認

念のため、プロシージャの動作を確認します。
確認を実施するために、上に掲載したプロシージャの中の一部を書き換えます。
実際に分割・削除を実行するための行である「EXECUTE STMT」の行を「#」文字を使って、コメント化します。

残念ながら、プロシージャの中を一部だけ書き換えることはできませんので、書き換えたプロシージャ全体を用意して、再度コンソールから流し込む形になります。

準備ができたら、いよいよ動作の確認です。
コンソールから、以下のように入力し、実行させてみます。

実行すると、画面上に、いくつかの表示が流れていくと思います。
そのうち、「LOG」と書かれているものは、記録用のログ出力ですので、大して気にする必要はありません。

注目するべき出力は、「@sql」と書かれているものです。
これが「パーティション分割・削除のために実行される SQL」に相当します。

実例を挙げます。
この記録の場合、history テーブルの pmax と名付けられているパーティション(MAXVALUE までの値を受け入れるように指定されていた最終パーティション)を2つに分け、clock の値が「1406041200」未満のものを「p20140723_00」と名づけた新規パーティションに、それを超える値 (1406041200 ~ MAXVALUE まで) のものを、従来通り「pmax」と名づけたパーティションへ、それぞれ分けるようにしなさい、といった指示になります。
1406041200 というのは、現在時刻から算出された時刻値です。例の場合には「2014/7/23 00:00:00」を示しています。
先に解説したとおり、現在時刻を基準に、分割日数分のデータを受け入れるための枠組として、新しいパーティションを作っているような処理になっています。

ここまでで、動作の確認も完了しました。
先ほどコメントアウトした処理を元の状態に戻し、実際に実行されるようにして、再度プロシージャを投入します。

準備ができたら、実際に実行してみます。
コンソールから、先ほどと同じように入力し、実行させてみます。
1度だけでなく、2・3度実行してみてください。そうすることによって、「今すぐは使わないけれど、近い将来に使われるためのパーティション」も含め、作成・準備されます。

時系列での分割 – プロシージャの自動実行

ここまでで、必要に応じて古いパーティションを捨て、新しいパーティションを準備する‥という作業が、プロシージャの実行1行で出来るようになりました。
残る問題は‥「え‥?これ‥毎日毎回ボクが手で実行するの‥?面倒くさい‥忘れたらどうなるの‥」ということです。
そうです。Zabbix は放っておけば、時々刻々と新しい監視データがサーバへ届き、溜まってしまいます。
それに合わせるように、日々、パーティションの状態をメンテナンスしなければいけません。
そこで、cron に指定して使えるようなスクリプトも併せて用意します。

任意のディレクトリに配置し、次のような形で、cron へ仕掛けてください。
実行時に、コンソールに出力された内容をログファイルへ記録するような形にしています。
スクリプト内では、まず、MySQL のコンソールを起動し、ストアドプロシージャを実行しています。完了後、過去の実行時に記録したログファイルのうち、古いものを削除しています。

おわりに

これで、やっとのことで、パーティショニングを用いた Zabbix データベースのメンテナンスを実現・組み込むことができました。
現行最新の Zabbix 2.2 では、DB 周りのパフォーマンスがかなり上がっており、ここまでチューニングしなければいけないことは稀ですが、1.8 や 2.0 では、かなりの効果があるのもまた事実です。

以下に、本記事にて掲載したソースコード・スクリプト類を、ファイルとして置いておきます。
参考までにご活用いただければ幸いです。

「ZabbixのDB(MySQL)をパーティショニングする(2)」への2件のフィードバック

コメントを残す