bike and clock

Amazon Aurora PostgreSQLでZabbixデータベースをパーティショニングしてみる

Amazon RDS,PostgreSQL,Zabbix

こんにちは。プラットフォーム技術部の佐藤(賢)です。

TimescaleDBが使えない環境でZabbixデータベースのパーティショニングをやってみましたので紹介します。

はじめに

Zabbixデータベースのパーティショニングとは

Zabbixのデータベースではデータが大量になると、古いデータを削除するハウスキーパーの処理負荷が問題になる場合があります。
この問題を対処するため、ZabbixネイティブではTimescaleDBが利用できるようになりました。
TimescaleDBはいわゆる時系列データベースです。データの時間範囲ごとにチャンクと呼ばれる領域に保存し、チャンク単位で削除を実施することで、ハウスキーパーの処理負荷を軽減できるようになっています。

実は「時系列単位にデータを保存して削除してハウスキーパー負荷軽減」という考え方自体は、TimescaleDB以前からありました。データベースの時系列パーティショニングを使うというものです。
最近ですと、MySQLネイティブの機能で時系列パーティショニングを実現する方法が以下の記事で紹介されています。

Partitioning a Zabbix MySQL(8) database with Perl or Stored Procedures – Zabbix Blog

TimescaleDBはPostgreSQLの拡張機能として実現されていますのでMySQLでは使えない、であればMySQLネイティブでパーティショニングして負荷軽減しようというものです。

またデータベースがMySQLの場合に限らず、拡張機能が提供されていないクラウドサービスなどではPostgreSQLベースの環境でも利用できない場合があります。
例えばAzure Database for PostgreSQLでは利用できますが、Amazon Aurora PostgreSQLでは利用できません。
「AWSのRDSでは使えないのか」と嘆いても仕方がありませんので、前述のMySQLパーティショニング記事のようにAmazon Aurora PostgreSQLでのZabbixデータベースパーティショニングにチャレンジしたいと思います。

MySQLでのパーティショニングでやっていること

先ほどのMySQLパーティショニング記事を紐解くと、おおざっぱに以下を実施していることがわかります。

  • history系、trends系テーブルのパーティショニング設定
  • パーティション管理情報を保存するためのテーブル作成(manage_partitions)
  • パーティション管理用のプロシージャー作成(create_next_partitionsやdrop_partitionsなど)
  • パーティション管理用プロシージャーを定期実行するイベント作成(e_part_manage)
  • Zabbix Webコンソールからhistory、trendsのハウスキーパーを無効化

基本的には、これらをPostgreSQLネイティブで実現すればよいです。

PostgreSQLで実現するには

PostgreSQLネイティブの場合は、拡張機能を使うことでパーティショニングの管理を簡素化できます。
そもそもMySQL用のプロシージャーなどをPostgreSQL用に移植するのが大変ですが、それらを自前で構築する必要がありません。
利用する拡張機能は以下の2つです。Amazon Aurora PostgreSQLでも利用可能です。

拡張機能提供機能
pg_partman管理用テーブル、管理用プロシージャーなどを提供します
pg_cronOS上cronのような定期実行環境をPostgreSQL上で提供します
PostgreSQL拡張機能

それぞれの拡張機能に関してはAWS公式ドキュメントでも紹介されていますので適宜ご参照ください。

環境

今回構築する環境は以下のとおりです。

  • Amazon Aurora PostgreSQL 14
  • Red Hat Enterprise Linux 9
  • Zabbix 6.4 beta4

Zabbixパッケージ群のインストールは完了している状態から開始します。バージョンは構築開始時(2022年12月初旬)の最新版です。またPostgreSQLデータベースは以下で構築していきます。

  • マスターユーザー: postgres
  • Zabbixデータベースユーザー名: zabbix
  • Zabbixデータベース名: zabbix
  • Zabbixデータベーススキーマ名: zabbix

スキーマは将来的にpublic利用が非推奨になっていきますので、zabbixスキーマを用意して構築します。

パーティショニング単位と保存期間は以下の通りにします。

テーブル種別パーティショニング単位事前作成パーティション数保存期間
history系1日単位7個90日
trends系1月単位3個2年
パーティショニング設定

構築作業の実際

PostgreSQL拡張機能利用の準備

Auroraで使用しているDB cluster parameter groupでshared_preload_librariesとcron.database_nameを設定します。AWSデフォルト提供のパラメーターグループを使っている場合は変更できませんので、新たに作成してPostgreSQLクラスターに設定します。

DB cluster parameter groupの変更内容
DB cluster parameter groupの変更内容

shared_preload_librariesにpg_cronを追加し、pg_cron拡張でスキーマを配置するデータベース(cron.database_name)をZabbixデータベース名に変更しています。

Zabbixデータベース作成と拡張機能の有効化

マスターユーザーで実施します。PostgreSQLインスタンスのホスト名は$DBHost変数に設定しています。

$ psql -h $DBHost -U postgres
-- ロール、データベース作成
CREATE ROLE zabbix WITH LOGIN PASSWORD '********';
GRANT zabbix TO postgres;
CREATE DATABASE zabbix OWNER zabbix;
-- 接続データベース変更
\c zabbix
-- pg_partman拡張有効化
CREATE SCHEMA partman;
CREATE EXTENSION pg_partman SCHEMA partman;
GRANT ALL ON SCHEMA partman TO zabbix;
GRANT ALL ON ALL TABLES IN SCHEMA partman TO zabbix;
GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA partman TO zabbix;
GRANT EXECUTE ON ALL PROCEDURES IN SCHEMA partman TO zabbix;
GRANT TEMPORARY ON DATABASE zabbix TO zabbix;
-- pg_cron拡張有効化
CREATE EXTENSION pg_cron;
GRANT USAGE ON SCHEMA cron TO zabbix;
-- zabbixスキーマ作成(public利用回避)
CREATE SCHEMA AUTHORIZATION zabbix;

Zabbixデータベースへの初期スキーマ作成とデータ投入

zabbixユーザーで実施します。スキーマはサーチパスのデフォルトが"$user, public"ですので、ユーザー名と同じ名前のスキーマを用意しておくと、そちらが優先的に使用されます。

$ zcat /usr/share/zabbix-sql-scripts/postgresql/server.sql.gz | psql -h $DBHost -U zabbix zabbix

zabbixユーザーで接続しhistory、trends系テーブルをパーティショニングで再作成します。

$ psql -h $DBHost -U zabbix zabbix
-- パーティショニング対象テーブルをいったん削除
DROP TABLE history;
DROP TABLE history_uint;
DROP TABLE history_str;
DROP TABLE history_log;
DROP TABLE history_text;
DROP TABLE trends;
DROP TABLE trends_uint;
-- パーティショニング対象テーブルを再作成
CREATE TABLE history (
        itemid                   bigint                                    NOT NULL,
        clock                    integer         DEFAULT '0'               NOT NULL,
        value                    DOUBLE PRECISION DEFAULT '0.0000'          NOT NULL,
        ns                       integer         DEFAULT '0'               NOT NULL,
        PRIMARY KEY (itemid,clock,ns)
) PARTITION BY RANGE (clock);
CREATE TABLE history_uint (
        itemid                   bigint                                    NOT NULL,
        clock                    integer         DEFAULT '0'               NOT NULL,
        value                    numeric(20)     DEFAULT '0'               NOT NULL,
        ns                       integer         DEFAULT '0'               NOT NULL,
        PRIMARY KEY (itemid,clock,ns)
) PARTITION BY RANGE (clock);
CREATE TABLE history_str (
        itemid                   bigint                                    NOT NULL,
        clock                    integer         DEFAULT '0'               NOT NULL,
        value                    varchar(255)    DEFAULT ''                NOT NULL,
        ns                       integer         DEFAULT '0'               NOT NULL,
        PRIMARY KEY (itemid,clock,ns)
) PARTITION BY RANGE (clock);
CREATE TABLE history_log (
        itemid                   bigint                                    NOT NULL,
        clock                    integer         DEFAULT '0'               NOT NULL,
        timestamp                integer         DEFAULT '0'               NOT NULL,
        source                   varchar(64)     DEFAULT ''                NOT NULL,
        severity                 integer         DEFAULT '0'               NOT NULL,
        value                    text            DEFAULT ''                NOT NULL,
        logeventid               integer         DEFAULT '0'               NOT NULL,
        ns                       integer         DEFAULT '0'               NOT NULL,
        PRIMARY KEY (itemid,clock,ns)
) PARTITION BY RANGE (clock);
CREATE TABLE history_text (
        itemid                   bigint                                    NOT NULL,
        clock                    integer         DEFAULT '0'               NOT NULL,
        value                    text            DEFAULT ''                NOT NULL,
        ns                       integer         DEFAULT '0'               NOT NULL,
        PRIMARY KEY (itemid,clock,ns)
) PARTITION BY RANGE (clock);
CREATE TABLE trends (
        itemid                   bigint                                    NOT NULL,
        clock                    integer         DEFAULT '0'               NOT NULL,
        num                      integer         DEFAULT '0'               NOT NULL,
        value_min                DOUBLE PRECISION DEFAULT '0.0000'          NOT NULL,
        value_avg                DOUBLE PRECISION DEFAULT '0.0000'          NOT NULL,
        value_max                DOUBLE PRECISION DEFAULT '0.0000'          NOT NULL,
        PRIMARY KEY (itemid,clock)
) PARTITION BY RANGE (clock);
CREATE TABLE trends_uint (
        itemid                   bigint                                    NOT NULL,
        clock                    integer         DEFAULT '0'               NOT NULL,
        num                      integer         DEFAULT '0'               NOT NULL,
        value_min                numeric(20)     DEFAULT '0'               NOT NULL,
        value_avg                numeric(20)     DEFAULT '0'               NOT NULL,
        value_max                numeric(20)     DEFAULT '0'               NOT NULL,
        PRIMARY KEY (itemid,clock)
) PARTITION BY RANGE (clock);

パーティション管理情報の設定

pg_partman拡張機能のcreate_parentファンクションを使うと、管理情報の登録と初期パーティションの作成がおこなえるのですが、メンテナンス処理の動作確認をおこないたいので少々細工します。

  • start_partitionsにパーティションの保存期限より以前の日付を指定して保存期限前の過去パーティションを作成
    • メンテナンス処理で期限切れのパーティションが削除されることを確認します
  • premakeは1を指定して将来用の初期パーティションは1つのみ作成し、そのあと管理情報を本来の値に再設定
    • メンテナンス処理でパーティションが追加作成されることを確認します
SELECT partman.create_parent(
 p_parent_table    => 'zabbix.history',
 p_control         => 'clock',
 p_type            => 'native',
 p_interval        => 'daily',
 p_start_partition := '2022-09-01',
 p_premake         => 1,
 p_epoch           => 'seconds');
SELECT partman.create_parent(
 p_parent_table    => 'zabbix.history_uint',
 p_control         => 'clock',
 p_type            => 'native',
 p_interval        => 'daily',
 p_start_partition := '2022-09-01',
 p_premake         => 1,
 p_epoch           => 'seconds');
SELECT partman.create_parent(
 p_parent_table    => 'zabbix.history_str',
 p_control         => 'clock',
 p_type            => 'native',
 p_interval        => 'daily',
 p_start_partition := '2022-09-01',
 p_premake         => 1,
 p_epoch           => 'seconds');
SELECT partman.create_parent(
 p_parent_table    => 'zabbix.history_log',
 p_control         => 'clock',
 p_type            => 'native',
 p_interval        => 'daily',
 p_start_partition := '2022-09-01',
 p_premake         => 1,
 p_epoch           => 'seconds');
SELECT partman.create_parent(
 p_parent_table    => 'zabbix.history_text',
 p_control         => 'clock',
 p_type            => 'native',
 p_interval        => 'daily',
 p_start_partition := '2022-09-01',
 p_premake         => 1,
 p_epoch           => 'seconds');
SELECT partman.create_parent(
 p_parent_table    => 'zabbix.trends',
 p_control         => 'clock',
 p_type            => 'native',
 p_interval        => 'monthly',
 p_start_partition := '2020-11-01',
 p_premake         => 1,
 p_epoch           => 'seconds');
SELECT partman.create_parent(
 p_parent_table    => 'zabbix.trends_uint',
 p_control         => 'clock',
 p_type            => 'native',
 p_interval        => 'monthly',
 p_start_partition := '2020-11-01',
 p_premake         => 1,
 p_epoch           => 'seconds');

各項目の意味は以下のとおりです。

項目名設定内容
p_parent_table親テーブル名
p_controlパーティションレンジを制御するカラム名
p_typenative固定(古いバージョンのPostgreSQLでトリガーベースでのパーティショニングを使用したい場合はpartmanを指定)
p_intervalパーティションの期間
p_start_partition開始パーティションの日時(:=日付でも可)
p_premake事前に作成するパーティション数
p_epochp_controlのデータがtimestamp形式以外の数値の場合に数値単位としてsecondsやnanosecondsなどを指定

これで初期パーティションも作成されました。
こちらの作業は2022年12月9日に実施したのですが、historyはhistory_p2022_12_09を中心に将来分1個、過去分はstart_partition時点から作成されていることが確認できます。

-- historyテーブル
\d+ zabbix.history
                                              Partitioned table "zabbix.history"
 Column |       Type       | Collation | Nullable |        Default        | Storage | Compression | Stats target | Description
--------+------------------+-----------+----------+-----------------------+---------+-------------+--------------+-------------
 itemid | bigint           |           | not null |                       | plain   |             |              |
 clock  | integer          |           | not null | 0                     | plain   |             |              |
 value  | double precision |           | not null | '0'::double precision | plain   |             |              |
 ns     | integer          |           | not null | 0                     | plain   |             |              |
Partition key: RANGE (clock)
Indexes:
    "history_pkey" PRIMARY KEY, btree (itemid, clock, ns)
Partitions: history_p2022_09_01 FOR VALUES FROM (1661990400) TO (1662076800),
            history_p2022_09_02 FOR VALUES FROM (1662076800) TO (1662163200),
            history_p2022_09_03 FOR VALUES FROM (1662163200) TO (1662249600),
            :
            history_p2022_12_08 FOR VALUES FROM (1670457600) TO (1670544000),
            history_p2022_12_09 FOR VALUES FROM (1670544000) TO (1670630400),
            history_p2022_12_10 FOR VALUES FROM (1670630400) TO (1670716800),
            history_default DEFAULT

-- trendsテーブル
\d+ zabbix.trends
                                                Partitioned table "zabbix.trends"
  Column   |       Type       | Collation | Nullable |        Default        | Storage | Compression | Stats target | Description
-----------+------------------+-----------+----------+-----------------------+---------+-------------+--------------+-------------
 itemid    | bigint           |           | not null |                       | plain   |             |              |
 clock     | integer          |           | not null | 0                     | plain   |             |              |
 num       | integer          |           | not null | 0                     | plain   |             |              |
 value_min | double precision |           | not null | '0'::double precision | plain   |             |              |
 value_avg | double precision |           | not null | '0'::double precision | plain   |             |              |
 value_max | double precision |           | not null | '0'::double precision | plain   |             |              |
Partition key: RANGE (clock)
Indexes:
    "trends_pkey" PRIMARY KEY, btree (itemid, clock)
Partitions: trends_p2020_11 FOR VALUES FROM (1604188800) TO (1606780800),
            trends_p2020_12 FOR VALUES FROM (1606780800) TO (1609459200),
            trends_p2021_01 FOR VALUES FROM (1609459200) TO (1612137600),
            :
            trends_p2022_11 FOR VALUES FROM (1667260800) TO (1669852800),
            trends_p2022_12 FOR VALUES FROM (1669852800) TO (1672531200),
            trends_p2023_01 FOR VALUES FROM (1672531200) TO (1675209600),
            trends_default DEFAULT

-- 登録された管理情報表示例
SELECT * FROM partman.part_config WHERE parent_table = 'zabbix.history'\gx
-[ RECORD 1 ]--------------+--------------------------------
parent_table               | zabbix.history
control                    | clock
partition_type             | native
partition_interval         | 1 day
constraint_cols            |
premake                    | 1
optimize_trigger           | 4
optimize_constraint        | 30
epoch                      | seconds
inherit_fk                 | t
retention                  |
retention_schema           |
retention_keep_table       | t
retention_keep_index       | t
infinite_time_partitions   | f
datetime_string            | YYYY_MM_DD
automatic_maintenance      | on
jobmon                     | t
sub_partition_set_full     | f
undo_in_progress           | f
trigger_exception_handling | f
upsert                     |
trigger_return_null        | t
template_table             | partman.template_zabbix_history
publications               |
inherit_privileges         | f
constraint_valid           | t
subscription_refresh       |
drop_cascade_fk            | f

パーティション設定の変更

パーティションの保存期限(retention値)はcreate_parent ファンクションでは設定できません。また保存期限経過後のテーブルやインデックスは削除せずにデタッチして維持する状態になっていますので(retention_keep_tableとretention_keep_indexのbool値)、SQLで管理情報を更新します。

メンテナンス処理の確認用に初期パーティション数(premake値)を1にしましたので、history系7個、trends系3個に増やします。また空のテーブルでもパーティションが作成されるようにinfinite_time_partitionsをtrueに設定します。

-- history系テーブルのパーティション設定
UPDATE partman.part_config
SET retention = '90 days',
    retention_keep_table = false,
    retention_keep_index = false,
    premake = 7,
    infinite_time_partitions = true
WHERE parent_table LIKE 'zabbix.history%';
-- trends系テーブルのパーティション設定
UPDATE partman.part_config
SET retention = '2 years',
    retention_keep_table = false,
    retention_keep_index = false,
    premake = 3,
    infinite_time_partitions = true
WHERE parent_table LIKE 'zabbix.trends%';

メンテナンススケジュール設定

pg_cron拡張機能を使ってメンテナンススケジュールを設定します。

-- パーティションのメンテナンススケジュール設定
SELECT cron.schedule('0 1 * * *', $$CALL partman.run_maintenance_proc()$$);

-- 履歴削除スケジュール設定
SELECT cron.schedule('0 2 * * *', $$DELETE
    FROM cron.job_run_details
    WHERE end_time < now() - interval '30 days'$$);

-- 確認
SELECT * FROM cron.job;
 jobid | schedule  |                     command                     | nodename  | nodeport | database | username | active | jobname
-------+-----------+-------------------------------------------------+-----------+----------+----------+----------+--------+---------
     1 | 0 1 * * * | CALL partman.run_maintenance_proc()             | localhost |     5432 | zabbix   | zabbix   | t      |
     2 | 0 2 * * * | DELETE                                         +| localhost |     5432 | zabbix   | zabbix   | t      |
       |           |     FROM cron.job_run_details                  +|           |          |          |          |        |
       |           |     WHERE end_time < now() - interval '30 days' |           |          |          |          |        |
(2 rows)

パーティションのメンテナンス用にpartman.run_maintenance_procプロシージャの実行と、pg_cronの実行履歴削除をそれぞれ1日1回実行するようにしています。

開発環境でAurora起動時間が短い場合などは、partman.run_maintenance_procプロシージャの実行頻度を多くして「必要なパーティションがまだ作られていない」といった状態に陥らないよう注意する必要があります。

ところでパーティションレンジに該当するパーティションが存在しなかった場合はどうなるのでしょうか。データ自体はデフォルトパーティションに保存されるので失われることはありません(_default付きのパーティション)。
ただデフォルトパーティションにデータが存在すると、そちらの値が新たにアタッチしたいパーティションレンジに含まれてしまう場合、パーティションのアタッチに失敗します。

大抵の場合、新規パーティション追加に失敗し続けるため、どこかでサービスを止めてデフォルトパーティション内のデータ退避、パーティションの手動アタッチと退避データのリストアといったリカバリー作業が必要になります。

実は経過を早く確認するために15分毎という短いパーティションレンジで動作確認している最中に、partman.run_maintenance_procプロシージャがこんなログを出し続けていることに気がつき、慌ててテーブルを削除して再作成したのは内緒です(データ移行は諦めました)。

ERROR: updated partition constraint for default partition "history_text_default" would be violated by some row
CONTEXT: SQL statement "ALTER TABLE zabbix.history_text ATTACH PARTITION zabbix.history_text_p2022_12_08_1630 FOR VALUES FROM ('1670484600') TO ('1670485500')"

初回メンテナンス実行後のパーティション状態

初回メンテナンス実行後にパーティション状態を確認すると、期限切れパーティションの削除と将来用パーティション作成が正常におこなわれていることが確認できます(メンテナンス実行日は2022年12月9日です)。

historyテーブルはhistory_p2022_09_09以前が削除され、history_p2022_12_16まで作成されました。

-- historyテーブル
\d+ zabbix.history
                                              Partitioned table "zabbix.history"
 Column |       Type       | Collation | Nullable |        Default        | Storage | Compression | Stats target | Description
--------+------------------+-----------+----------+-----------------------+---------+-------------+--------------+-------------
 itemid | bigint           |           | not null |                       | plain   |             |              |
 clock  | integer          |           | not null | 0                     | plain   |             |              |
 value  | double precision |           | not null | '0'::double precision | plain   |             |              |
 ns     | integer          |           | not null | 0                     | plain   |             |              |
Partition key: RANGE (clock)
Indexes:
    "history_pkey" PRIMARY KEY, btree (itemid, clock, ns)
Partitions: history_p2022_09_10 FOR VALUES FROM (1662768000) TO (1662854400),
            history_p2022_09_11 FOR VALUES FROM (1662854400) TO (1662940800),
            history_p2022_09_12 FOR VALUES FROM (1662940800) TO (1663027200),
            :
            history_p2022_12_14 FOR VALUES FROM (1670976000) TO (1671062400),
            history_p2022_12_15 FOR VALUES FROM (1671062400) TO (1671148800),
            history_p2022_12_16 FOR VALUES FROM (1671148800) TO (1671235200),
            history_default DEFAULT

trendsテーブルはtrends_p2020_11以前が削除され、trends_p2023_03まで作成されました。

-- trendsテーブル
\d+ zabbix.trends;
                                                Partitioned table "zabbix.trends"
  Column   |       Type       | Collation | Nullable |        Default        | Storage | Compression | Stats target | Description
-----------+------------------+-----------+----------+-----------------------+---------+-------------+--------------+-------------
 itemid    | bigint           |           | not null |                       | plain   |             |              |
 clock     | integer          |           | not null | 0                     | plain   |             |              |
 num       | integer          |           | not null | 0                     | plain   |             |              |
 value_min | double precision |           | not null | '0'::double precision | plain   |             |              |
 value_avg | double precision |           | not null | '0'::double precision | plain   |             |              |
 value_max | double precision |           | not null | '0'::double precision | plain   |             |              |
Partition key: RANGE (clock)
Indexes:
    "trends_pkey" PRIMARY KEY, btree (itemid, clock)
Partitions: trends_p2020_12 FOR VALUES FROM (1606780800) TO (1609459200),
            trends_p2021_01 FOR VALUES FROM (1609459200) TO (1612137600),
            trends_p2021_02 FOR VALUES FROM (1612137600) TO (1614556800),
            :
            trends_p2023_01 FOR VALUES FROM (1672531200) TO (1675209600),
            trends_p2023_02 FOR VALUES FROM (1675209600) TO (1677628800),
            trends_p2023_03 FOR VALUES FROM (1677628800) TO (1680307200),
            trends_default DEFAULT

Zabbix設定

Webフロントエンドの設定を実施しログインします。
左メニューの[Administration]>[Housekeeping]からHistoryとTrendsのハウスキーピング処理を無効化します(Enable internal housekeepingをオフ)。

ハウスキーパー設定
ハウスキーパー設定

メンテナンス処理にpg_partman_bgwではなくpg_cronを使う理由

pg_partmanにはbackground worker(pg_partman_bgw)が用意されていて、そちらをロードすることでメンテナンス処理を実行できるようになっていますが、AWSでは提供されておらずpg_cronを使うようになっています。

実はpg_cronはhot standby状態(RecoveryInProgress()応答がfalse)では動作しないようになっており、Reader instanceが存在するAWSのRDSでは都合がよいです。

RecoveryInProgress()の結果はpg_is_in_recoveryでも確認できるのですが、Reader instanceではこちらがfalseですので、pg_cron処理が実行されないことがわかります。

SELECT pg_is_in_recovery();
 pg_is_in_recovery
-------------------
 t
(1 row)

オンプレでレプリケーションクラスター構成をとる場合なども、pg_partman_bgwではなくpg_cronなどを使ってメンテナンス処理をおこなったほうがよいです。

パーティショニングの弊害

データベース機能でネイティブパーティショニングを実現する場合の弊害をZabbix視点で少しあげておきます。

アイテムの保存期間を個別に設定できない

本来ですとアイテムごとにヒストリーやトレンドの保存期限を設定できますが、パーティショニング時はテーブルごとに一律になります。アイテムのデータタイプで保存先テーブルが決まりますので、ホストやアイテム項目に関係なく、データタイプごとに一律になります。

将来のアップグレード対応が煩雑

Zabbixのバージョンアップではテーブル構成の変更などを自動で実行するようになっています。大抵SQL実行で処理されるのですが、これらはパーティショニングを考慮していません。エラーになる可能性もありますので、その場合は個別の手当てが必要になります。

ただ実際にはパーティショニングされた、おそらく大量のデータが保持されているhistoryやtrendsテーブルにそのまま自動アップグレードを施すことは現実的ではありません。ALTERでカラムの追加、削除やタイプ変更など実施したら途方もない時間がかかります。

従って現実的な移行方法としては、設定系のみ自動アップグレード後に新バージョンに合わせた再パーティショニングを施しデータ移行という段取りになります。パーティショング自体よりも、その起因となる大量のデータ移行をいかに効率よく実施するかが課題になります。

パーティショニングはZabbixサポート対象外

TimescaleDBの場合はそちらを前提とした処理コードがZabbixにも組み込まれていますので、TimescaleDB部分まで踏み込んだ調査や問題解決サポートも提供される可能性が高いです。内容次第ではありますが、少なくとも門前払いはないでしょう。

しかしながら、データベース機能を利用したパーティションニングはTimescaleDBと異なり、Zabbixの一切関与しないところで実装しているので自己解決していく必要があります。

最後に

Zabbixデータベースでのパーティショニングをご紹介しましたが、時系列パーティショニング自体は、通常のアプリケーション用途としても有用なケースが多数あると思われます。

AWS Database Blogにも時系列データを取り扱う際のpg_partman拡張の利用方法など紹介されていますので、ぜひご活用ください。

Designing high-performance time series data tables on Amazon RDS for PostgreSQL

さて、本記事の記載を始めてから約2ヵ月動かしてみましたが、パーティショニング処理は問題なく稼働しています。データが存在するhistoryやtrendsテーブルはもちろんのこと、データの存在しないhistory_logも旧パーティションの削除と将来パーティションの作成が順当におこなわれていました。この報告をもって本記事は終了いたします。

zabbix=> \d+ history;
                                              Partitioned table "zabbix.history"
 Column |       Type       | Collation | Nullable |        Default        | Storage | Compression | Stats target | Description
--------+------------------+-----------+----------+-----------------------+---------+-------------+--------------+-------------
 itemid | bigint           |           | not null |                       | plain   |             |              |
 clock  | integer          |           | not null | 0                     | plain   |             |              |
 value  | double precision |           | not null | '0'::double precision | plain   |             |              |
 ns     | integer          |           | not null | 0                     | plain   |             |              |
Partition key: RANGE (clock)
Indexes:
    "history_pkey" PRIMARY KEY, btree (itemid, clock, ns)
Partitions: history_p2022_11_08 FOR VALUES FROM (1667865600) TO (1667952000),
            history_p2022_11_09 FOR VALUES FROM (1667952000) TO (1668038400),
            history_p2022_11_10 FOR VALUES FROM (1668038400) TO (1668124800),
            :
            history_p2023_02_11 FOR VALUES FROM (1676073600) TO (1676160000),
            history_p2023_02_12 FOR VALUES FROM (1676160000) TO (1676246400),
            history_p2023_02_13 FOR VALUES FROM (1676246400) TO (1676332800),
            history_default DEFAULT

zabbix=> \d+ trends;
                                                Partitioned table "zabbix.trends"
  Column   |       Type       | Collation | Nullable |        Default        | Storage | Compression | Stats target | Description
-----------+------------------+-----------+----------+-----------------------+---------+-------------+--------------+-------------
 itemid    | bigint           |           | not null |                       | plain   |             |              |
 clock     | integer          |           | not null | 0                     | plain   |             |              |
 num       | integer          |           | not null | 0                     | plain   |             |              |
 value_min | double precision |           | not null | '0'::double precision | plain   |             |              |
 value_avg | double precision |           | not null | '0'::double precision | plain   |             |              |
 value_max | double precision |           | not null | '0'::double precision | plain   |             |              |
Partition key: RANGE (clock)
Indexes:
    "trends_pkey" PRIMARY KEY, btree (itemid, clock)
Partitions: trends_p2021_02 FOR VALUES FROM (1612137600) TO (1614556800),
            trends_p2021_03 FOR VALUES FROM (1614556800) TO (1617235200),
            trends_p2021_04 FOR VALUES FROM (1617235200) TO (1619827200),
            :
            trends_p2023_03 FOR VALUES FROM (1677628800) TO (1680307200),
            trends_p2023_04 FOR VALUES FROM (1680307200) TO (1682899200),
            trends_p2023_05 FOR VALUES FROM (1682899200) TO (1685577600),
            trends_default DEFAULT

zabbix=> select count(*) from history_log;
 count
-------
     0
(1 row)

zabbix=> \d+ history_log;
                                                 Partitioned table "zabbix.history_log"
   Column   |         Type          | Collation | Nullable |        Default        | Storage  | Compression | Stats target | Description
------------+-----------------------+-----------+----------+-----------------------+----------+-------------+--------------+-------------
 itemid     | bigint                |           | not null |                       | plain    |             |              |
 clock      | integer               |           | not null | 0                     | plain    |             |              |
 timestamp  | integer               |           | not null | 0                     | plain    |             |              |
 source     | character varying(64) |           | not null | ''::character varying | extended |             |              |
 severity   | integer               |           | not null | 0                     | plain    |             |              |
 value      | text                  |           | not null | ''::text              | extended |             |              |
 logeventid | integer               |           | not null | 0                     | plain    |             |              |
 ns         | integer               |           | not null | 0                     | plain    |             |              |
Partition key: RANGE (clock)
Indexes:
    "history_log_pkey" PRIMARY KEY, btree (itemid, clock, ns)
Partitions: history_log_p2022_11_08 FOR VALUES FROM (1667865600) TO (1667952000),
            history_log_p2022_11_09 FOR VALUES FROM (1667952000) TO (1668038400),
            history_log_p2022_11_10 FOR VALUES FROM (1668038400) TO (1668124800),
            :
            history_log_p2023_02_11 FOR VALUES FROM (1676073600) TO (1676160000),
            history_log_p2023_02_12 FOR VALUES FROM (1676160000) TO (1676246400),
            history_log_p2023_02_13 FOR VALUES FROM (1676246400) TO (1676332800),
            history_log_default DEFAULT

zabbix=>

  • 株式会社アークシステムの予約・来訪管理システム BRoomHubs
  • 低コスト・短納期で提供するまるごとおまかせZabbix