MySQL 最適化 Zabbix パーティション最適化

MySQL 最適化 Zabbix パーティション最適化

zabbix を利用する上での最大のボトルネックはデータベースです。zabbix のデータストレージとアラームを整備することで、zabbix を利用した監視システムを構築することができます。現在、Zabbix データは主に履歴とトレンドの 2 つのテーブルに保存されています。時間が経つにつれて、これら 2 つのテーブルが非常に大きくなり、パフォーマンスが非常に低下し、監視の使用に影響を及ぼします。 MySQL をチューニングすると、Zabbix のパフォーマンスが大幅に向上します。この記事では、チューニングに MySQL をパーティション分割する方法を使用します。

原理

Zabbix の履歴テーブルと傾向テーブルを日付ごとに 1 つのパーティションに分割し、合計 90 日分のパーティションを保持します。

詳細な操作手順

運用上の影響:オンライン運用は可能ですが、MySQL の読み書き速度が遅くなり、Zabbix のパフォーマンスが低下します。影響時間はデータのサイズによって異なりますが、概ね 2 時間程度です。

最初のステップ

zabbixサーバーのデータベースにログインし、MySQLの設定を統一する

cat > /etc/my.cnf<<EOF
[mysqld]
データディレクトリ=/data/mysql
ソケット=/var/lib/mysql/mysql.sock
デフォルトのストレージエンジン = innodb
照合サーバー = utf8_general_ci
init-connect = '名前をutf8に設定'
文字セットサーバー = utf8
シンボリックリンク=0
最大接続数=4096
innodb_buffer_pool_size=12G
最大許容パケット = 32M
結合バッファサイズ=2M
ソートバッファサイズ=2M 
クエリキャッシュサイズ = 64M  
クエリキャッシュ制限 = 4M  
スレッド同時実行性 = 8
テーブルオープンキャッシュ=1024
innodb_flush_log_at_trx_commit = 0

長いクエリ時間 = 1
ログスロークエリ=/data/mysql/mysql-slow.log 

[mysqld_safe]
ログエラー=/var/log/mariadb/mariadb.log
pidファイル=/var/run/mariadb/mariadb.pid

#[mysql]
#ソケット=/data/mysql/mysql.sock
#
# configディレクトリからすべてのファイルをインクルードする
#
!includedir /etc/my.cnf.d
終了

注意: innodb_buffer_pool_size = 物理メモリの1/3に変更してください。

ステップ2

まず、zabbix のバージョンを確認してください。この操作の zabbix のバージョンは 3.2.0 より大きくなければなりません。この操作は、3.2 未満のバージョンにはインストールできません。オンラインのデフォルトは zabbix-3.2.6 です。

a. ストアドプロシージャをインポートする

#cat パーティション.sql
区切り文字 $$
CREATE PROCEDURE `partition_create`(SCHEMANAME varchar(64), TABLENAME varchar(64), PARTITIONNAME varchar(64), CLOCK int)
始める
    /*
     SCHEMANAME = 変更を加えるDBスキーマ
     TABLENAME = 削除する可能性のあるパーティションを持つテーブル
     PARTITIONNAME = 作成するパーティションの名前
    */
    /*
     パーティションがまだ存在していないことを確認する
    */

    INT を宣言します。
    COUNT(1)をRETROWSに選択
    information_schema.partitions から
    ここで、table_schema = SCHEMANAME かつ table_name = TABLENAME かつ、partition_description >= CLOCK;

    RETROWS = 0の場合
        /*
          1. パーティションが作成されたことを示すメッセージを出力します。
          2. パーティションを作成するための SQL を作成します。
          3. 2 の SQL を実行します。
        */
        SELECT CONCAT( "partition_create(", SCHEMANAME, ",", TABLENAME, ",", PARTITIONNAME, ",", CLOCK, ")" )AS msg;
        SET @sql = CONCAT( 'ALTER TABLE ', SCHEMANAME, '.', TABLENAME, ' ADDPARTITION (PARTITION ', PARTITIONNAME, ' VALUES LESS THAN (', CLOCK, '));' );
        @sql から STMT を準備します。
        STMT を実行します。
        割り当てを解除し、STMT を準備します。
    終了の場合;
終わり$$
区切り文字 ;

区切り文字 $$
CREATE PROCEDURE `partition_drop`(SCHEMANAME VARCHAR(64), TABLENAME VARCHAR(64), DELETE_BELOW_PARTITION_DATE BIGINT)
始める
    /*
      SCHEMANAME = 変更を加えるDBスキーマ
     TABLENAME = 削除する可能性のあるパーティションを持つテーブル
     DELETE_BELOW_PARTITION_DATE = この日付より古い名前のパーティションを削除します (yyyy-mm-dd)
    */
    done INT DEFAULT FALSE を宣言します。
    drop_part_name VARCHAR(16) を宣言します。

    /*
     日付より古いすべてのパーティションのリストを取得します
     DELETE_BELOW_PARTITION_DATEで、すべてのパーティションにプレフィックスが付けられます。
      「p」なので、SUBSTRING TO を使用してその文字を削除します。
    */
    myCursor CURSOR FOR を宣言する
        パーティション名を選択
        information_schema.partitions から
        ここで、table_schema = SCHEMANAME、table_name = TABLENAME、ANDCAST(SUBSTRING(partition_name FROM 2) AS UNSIGNED) <DELETE_BELOW_PARTITION_DATE;
    NOT FOUND SET done = TRUE の継続ハンドラーを宣言します。

    /*
     パーティションを削除する必要があるときのための基本を作成します。また、
     @drop_partitionsは、すべてのパーティションのカンマ区切りのリストを保持します。
     削除する必要があります。
    */
    SET @alter_header = CONCAT("ALTER TABLE ", SCHEMANAME,.", TABLENAME, " DROP PARTITION ");
    @drop_partitions を設定します。

    /*
     古すぎるすべてのパーティションをループし始めます。
    */
    myCursor を開きます。
    read_loop: ループ
        myCursor を drop_part_name に FETCH します。
        完了したら
            read_loop を終了します。
        終了の場合;
        SET @drop_partitions = IF(@drop_partitions = "",drop_part_name, CONCAT(@drop_partitions, ",", drop_part_name));
    ループを終了;
    IF @drop_partitions != "" の場合
        /*
          1. 必要なパーティションをすべて削除する SQL を作成します。
          2. SQL を実行してパーティションを削除します。
          3. 削除されたテーブルパーティションを出力します。
        */
        SET @full_sql = CONCAT(@alter_header, @drop_partitions, ";");
        @full_sql から STMT を準備します。
        STMT を実行します。
        割り当てを解除し、STMT を準備します。

        SELECT CONCAT(SCHEMANAME, ".", TABLENAME) AS `table`,@drop_partitions AS `partitions_deleted`;
    それ以外
        /*
          パーティションは削除されないので、「N/A」(該当なし)と出力して示す。
          変更は行われなかった。
        */
        SELECT CONCAT(SCHEMANAME, ".", TABLENAME) AS `table`,"N/A" AS `partitions_deleted`;
    終了の場合;
終わり$$
区切り文字 ;


区切り文字 $$
CREATE PROCEDURE `partition_maintenance`(SCHEMA_NAME VARCHAR(32), TABLE_NAME VARCHAR(32), KEEP_DATA_DAYS INT, HOURLY_INTERVAL INT, CREATE_NEXT_INTERVALS INT)
始める
    OLDER_THAN_PARTITION_DATE VARCHAR(16) を宣言します。
    PARTITION_NAME VARCHAR(16) を宣言します。
    OLD_PARTITION_NAME VARCHAR(16) を宣言します。
    LESS_THAN_TIMESTAMP INT を宣言します。
    CUR_TIME INT を宣言します。

    呼び出しpartition_verify(SCHEMA_NAME,TABLE_NAME,HOURLY_INTERVAL);
    CUR_TIME = UNIX_TIMESTAMP(DATE_FORMAT(NOW(), '%Y-%m-%d 00:00:00')) を設定します。

    @__間隔を 1 に設定します。
    create_loop: ループ
        @__interval > CREATE_NEXT_INTERVALS の場合
            create_loop を終了します。
        終了の場合;

        LESS_THAN_TIMESTAMP を CUR_TIME + (HOURLY_INTERVAL * @__interval *3600) に設定します。
        PARTITION_NAME を FROM_UNIXTIME(CUR_TIME + HOURLY_INTERVAL *(@__interval - 1) * 3600, 'p%Y%m%d%H00') に設定します。
        IF(PARTITION_NAME != OLD_PARTITION_NAME)の場合
            パーティション作成を呼び出します(SCHEMA_NAME、TABLE_NAME、PARTITION_NAME、LESS_THAN_TIMESTAMP);
        終了の場合;
        @__interval=@__interval+1 を設定します。
        OLD_PARTITION_NAME = PARTITION_NAME を設定します。
    ループを終了;

    OLDER_THAN_PARTITION_DATE=DATE_FORMAT(DATE_SUB(NOW(), INTERVALKEEP_DATA_DAYS DAY), '%Y%m%d0000') を設定します。
    パーティションドロップを呼び出します(SCHEMA_NAME、TABLE_NAME、OLDER_THAN_PARTITION_DATE);

終わり$$
区切り文字 ;

区切り文字 $$
CREATE PROCEDURE `partition_verify`(SCHEMANAME VARCHAR(64), TABLENAME VARCHAR(64), HOURLYINTERVAL INT(11))
始める
    PARTITION_NAME VARCHAR(16) を宣言します。
    RETROWS INT(11) を宣言します。
    FUTURE_TIMESTAMP TIMESTAMP を宣言します。

    /*
    * 指定された SCHEMANAME.TABLENAME にパーティションが存在するかどうかを確認します。
    */
    COUNT(1)をRETROWSに選択
    information_schema.partitions から
    table_schema = SCHEMANAME かつ table_name = TABLENAME かつpartition_name が NULL の場合;

    /*
    * パーティションが存在しない場合は、テーブルをパーティション分割します。
    */
    IFRETROWS = 1 の場合
        /*
        * 現在の日付 00:00:00 を取得し、それに HOURLYINTERVAL を追加します。これが、値を保存するタイムスタンプです。
        * 1日の始まりに基づいてパーティション分割を開始します。これは、ランダムなパーティションを生成したくないためです。
        * 必ずしも希望するパーティション名と一致しない場合があります(例:時間間隔が24時間の場合、
        * 他のすべてのパーティションが「p201403280000」のようになるのに、「p201403270600」という名前のパーティションが作成されてしまいます。
        */
        FUTURE_TIMESTAMP を TIMESTAMPADD(HOUR, HOURLYINTERVAL,CONCAT(CURDATE(), " ", '00:00:00')) に設定します。
        PARTITION_NAME = DATE_FORMAT(CURDATE(), 'p%Y%m%d%H00') を設定します。

        -- パーティションクエリを作成する
        SET @__PARTITION_SQL = CONCAT("ALTER TABLE ", SCHEMANAME,".", TABLENAME, " PARTITION BY RANGE(`clock`)");
        SET @__PARTITION_SQL = CONCAT(@__PARTITION_SQL, "(PARTITION ",PARTITION_NAME, " 値が (",UNIX_TIMESTAMP(FUTURE_TIMESTAMP), ") より小さい);");

        -- パーティションクエリを実行する
        @__PARTITION_SQL からステートメントを準備します。
        STMT を実行します。
        割り当てを解除し、STMT を準備します。
    終了の場合;
終わり$$
区切り文字 ;

区切り文字 $$
CREATE PROCEDURE `partition_maintenance_all`(SCHEMA_NAME VARCHAR(32))
始める
        呼び出しpartition_maintenance(SCHEMA_NAME, 'history', 90, 24, 14);
        呼び出しpartition_maintenance(SCHEMA_NAME, 'history_log', 90, 24, 14);
        呼び出しpartition_maintenance(SCHEMA_NAME, 'history_str', 90, 24, 14);
        パーティションメンテナンスを呼び出します(SCHEMA_NAME、'history_text'、90、24、14)。
        パーティションメンテナンスを呼び出します(SCHEMA_NAME、'history_uint'、90、24、14)。
        呼び出しpartition_maintenance(SCHEMA_NAME, 'trends', 730, 24, 14);
        呼び出しpartition_maintenance(SCHEMA_NAME, 'trends_uint', 730, 24, 14);
終わり$$
区切り文字 ;

上記の内容には、パーティションを作成するためのストアド プロシージャが含まれています。上記の内容をpartition.sqlにコピーし、次のように実行します。

mysql -uzabbix -pzabbix zabbix < パーティション.sql

b. 次のように、毎日 01:01 に実行する crontable を追加します。

crontab -l > crontab.txt 
cat >> crontab.txt <<EOF
#zabbix パーティションメンテナンス
01 01 * * * mysql -uzabbix -pzabbix zabbix -e"CALL partition_maintenance_all('zabbix')" &>/dev/null
終了
cat crontab.txt |crontab

注: mysqlのzabbixユーザーのパスワードは実際の環境に応じて設定されます

c. 最初に 1 回実行します (最初の実行には時間がかかるため、nohup を使用して実行します)。

nohup mysql -uzabbix -pzabbix zabbix -e "CALLpartition_maintenance_all('zabbix')" &> /root/partition.log&

注: /root/partition.logの出力を確認してください。

d. 結果を表示する

mysql にログインし、次のように履歴やその他のテーブルを表示します。

MariaDB [zabbix]> テーブル作成履歴の表示
| 履歴 | テーブル `history` を作成する (
 `itemid` bigint(20) 符号なし NOT NULL,
 `clock`int(11) NOT NULL デフォルト '0',
 `value` double(16,4) NOT NULL デフォルト '0.0000',
 `ns`int(11) NOT NULL デフォルト '0',
 KEY`history_1` (`itemid`,`clock`)
) エンジン=InnoDB デフォルト文字セット=utf8
/*!50100 範囲によるパーティション分割 (`clock`)
(パーティション p201708280000 値は(1503936000)未満です) エンジン = InnoDB、
 パーティション p201708290000 値は(1504022400)未満です エンジン = InnoDB、
 パーティション p201708300000 値は(1504108800)未満です エンジン = InnoDB、
 パーティション p201708310000 値は(1504195200)未満です エンジン = InnoDB、
 パーティション p201709010000 値は(1504281600)未満です エンジン = InnoDB、
 パーティション p201709020000 値は(1504368000)未満です エンジン = InnoDB、
 パーティション p201709030000 値は(1504454400)未満です エンジン = InnoDB、
 パーティション p201709040000 値は(1504540800)未満です エンジン = InnoDB、
 パーティション p201709050000 値は(1504627200)未満です エンジン = InnoDB、
 パーティション p201709060000 値は(1504713600)未満です エンジン = InnoDB、
 パーティション p201709070000 値は(1504800000)未満です エンジン = InnoDB、
 パーティション p201709080000 値は(1504886400)未満です エンジン = InnoDB、
 パーティション p201709090000 値は(1504972800)未満です エンジン = InnoDB、
 パーティション p201709100000 値は(1505059200)未満です エンジン = InnoDB、
 パーティション p201709110000 値は (1505145600) 未満です エンジン = InnoDB) */ |

多数の PARTITION フィールドが見つかりました。これは、構成が正しいことを示しています。 MySQL のスロークエリに注意してください。通常、スロークエリは運用開始 2 日目に発生します。このとき、Zabbix ダッシュボードの応答速度は非常にスムーズになっているはずです。

以下もご興味があるかもしれません:
  • MySQL の高度な機能 - データ テーブル パーティショニングの概念とメカニズムの詳細な説明
  • MySql テーブル、データベース、シャーディング、パーティショニングの知識の詳細な説明
  • MySql テーブル、データベース、シャーディング、パーティショニングの知識ポイントの紹介
  • MySQLテーブルシャーディングとパーティショニングの具体的な実装方法
  • Navicat による MySQL パーティショニングの実践
  • MySQL パーティションテーブルの正しい使用方法
  • MySQL パーティション フィールド列に別のインデックスを作成する必要がありますか?
  • MySQL データベース テーブルのパーティション分割に関する考慮事項 [推奨]
  • MySQL データ テーブル パーティション テクノロジーの簡単な分析
  • MySQL データテーブルのパーティション戦略と利点と欠点の分析

<<:  LinuxテキストエディタVimの詳しい説明

>>:  Javascript で関数のカリー化とデカリー化を実装する方法

推薦する

収集する価値のあるCSS命名規則(ルール) よく使われるCSS命名規則

CSS命名規則(ルール) よく使われるCSS命名規則ヘッダー: ヘッダーコンテンツ: コンテンツ/コ...

新しいユーザーを作成し、MySQLに権限を付与する最も簡単な方法

ユーザーを作成します: 'oukele' によって識別されるユーザー 'ou...

Vueはページング機能を実装する

この記事の例では、ページング機能を実装するためのVueの具体的なコードを参考までに共有しています。具...

ReactでCSSスタイルを動的に変更する2つの方法の詳細な説明

最初の方法: デモとしてボタンをクリックしてテキストを表示または非表示にするクラスを動的に追加します...

CSSマウスを画像の上に置いたときにマスクレイヤー効果を追加する実装

まず効果を見てみましょう: マウスを画像の上に移動すると、影の効果とテキスト/アイコンが追加されます...

Centos7 に mysql と mysqlclient をインストールする際に遭遇する落とし穴の概要

1. MySQL Yumリポジトリを追加するMySQL公式サイト>ダウンロード>MySQ...

CentOS ベースの OpenStack 環境の展開に関する詳細なチュートリアル (OpenStack のインストール)

エフェクト表示: 環境準備コントローラーノード: 6GB 4時間60GB/30GB/30GB計算ノー...

フォーム要素属性の読み取り専用と無効の使用の比較

1) 適用範囲:読み取り専用:input[type="text"],input[...

CSS テキスト装飾 text-decoration と text-emphasis の詳細な説明

CSS では、テキストは私たちが毎日扱う最も一般的なものの 1 つです。テキストの場合、テキストの装...

Vueはページキャッシュ機能を実装する

この記事の例では、ページキャッシュ機能を実装するためのVueの具体的なコードを参考までに共有していま...

case when文のエラー問題の詳細な説明

序文MySQL データベースでは、if else のような判断演算を使用することがあります。では、M...

MYSQL データベースの基礎 - 結合操作の原理

結合では、ネスト ループ結合アルゴリズムが使用されます。ネスト ループ結合には 3 つの種類がありま...

Dockerコンテナのホスト間マルチネットワークセグメント通信ソリューションの詳細説明

1. マックヴラン前回のブログ投稿で紹介した Docker コンテナのホスト間通信を実現するための ...

MySQLの自動増分主キーIDはこのように処理されません

MySQLの自動増分主キーIDは段階的に増加しません1. はじめにMySQL データベースにデータを...