MySQLでのトリガーvsストアドプロシージャのパフォーマンス


11

ここのDBA.StackExchangeの投稿(レコードのリビジョン番号を維持するためのトリガーのベストプラクティスは何ですか?)は、MySQLのパフォーマンスに関する興味深い質問(少なくとも、私にとって興味深い)を生み出しました。

コンテキストは、更新される行ごとにテーブルにレコードを挿入することです。行が更新される前に、以前の値を保存してから、列の1つ(「バージョン」列)を増分します。

これをトリガー内で実行すると、うまく機能します。MySQLの場合、トリガーはごとに行われるため、簡単に解決できます。現在テーブルにあるデータを選択し、それをロギングテーブルに挿入して、新しいデータの「バージョン」列を更新します。

ただし、このロジックをストアドプロシージャに移動することは可能です。その場合、挿入を実行してから、テーブルの「バージョン」列をインクリメントします。全体がベースに設定されます。

それで、この挿入を実行することになると、セットベースのストアドプロシージャアプローチまたはトリガーベースのアプローチを使用する方がパフォーマンスが良くなりますか?

この質問はMySQLに関するものです(行ごとのトリガーがあるため)。ただし、他の行ごとのトリガーDBMSにも当てはまります。


1
バージョニングロジックをストアドプロシージャにプッシュする際に注意すべき点の1つは、誰かが監査メカニズムをバイパスして、なんらかの理由でテーブルに直接書き込みを行う場合、どの程度控えめになるかです。
billinkc '

同意する。しかし、スケールの反対側では、特定の状況でこのロギングを意図的にバイパスしたい場合があります。もちろん、それはまったく別の質問です。私は本当にパフォーマンスへの影響に興味があります。
リチャード

回答:


7

簡単にするために、トリガーは、データベースの変更のあらゆる種類の追跡を実装するための方法です。ただし、トリガーを使用すると、内部で何が起こるかを認識する必要があります。

MySQLストアドプロシージャプログラミングによれば、256ページの「トリガーオーバーヘッド」の下には次のように書かれています。

トリガーは、必要に応じて、適用するDMLステートメントにオーバーヘッドを追加することを覚えておくことが重要です。実際のオーバーヘッドの量はトリガーの性質に依存しますが、---すべてのMySQLトリガーがFOR EACH ROWを実行するため---オーバーヘッドは、大量の行を処理するステートメントに対して急速に蓄積する可能性があります。したがって、トリガーに高価なSQLステートメントや手続きコードを配置することは避けてください。

トリガーオーバーヘッドの詳しい説明は、529〜531ページに記載されています。そのセクションの結論は次のとおりです。

ここでの教訓は次のとおりです。トリガーコードはDMLステートメントの影響を受ける行ごとに1回実行されるため、トリガーは簡単にDMLパフォーマンスの最も重要な要素になる可能性があります。トリガー本体内のコードは可能な限り軽量である必要があり、特にトリガー内のすべてのSQLステートメントは、可能な限りインデックスによってサポートされる必要があります。

本に記載されていないのは、トリガーを使用する際のもう1つの要因です。監査ロギングに関しては、データの記録先に注意してください。MyISAMテーブルにログを記録することを選択した場合、MyISAMテーブルへの各INSERTは、INSERT中にフルテーブルロックを生成するためです。これは、高トラフィック、高トランザクション環境で深刻なボトルネックになる可能性があります。さらに、トリガーがInnoDBテーブルに対するものであり、トリガー内からMyISAMの変更をログに記録する場合、これはACIDコンプライアンスを密かに無効にします(つまり、ブロックトランザクションを自動コミット動作に減らします)。これはロールバックできません。

InnoDBテーブルでトリガーを使用して変更をロギングする場合

  • ログに記録するテーブルもInnoDBです
  • 自動コミットがオフになっています
  • START TRANSACTION ... COMMIT / ROLLBACKブロックを完全に設定している

このようにして、監査ログはメインテーブルと同様にCOMMIT / ROLLBACKの恩恵を受けることができます。

ストアドプロシージャの使用に関しては、追跡されるテーブルに対してDMLのすべてのポイントでストアドプロシージャを入念に呼び出す必要があります。何万行ものアプリケーションコードに直面しても、ロギングの変更を見逃してしまいがちです。このようなコードをトリガーに配置すると、これらすべてのDMLステートメントを見つける必要がなくなります。

警告

トリガーの複雑さによっては、ボトルネックになる可能性があります。監査ログのボトルネックを軽減したい場合は、何かできることがあります。ただし、インフラストラクチャを少し変更する必要があります。

汎用ハードウェアを使用して、さらに2つのDBサーバーを作成します。

これにより、監査ログによるメインデータベース(MD)での書き込みI / Oが削減されます。これを実現する方法は次のとおりです。

ステップ01)メインデータベースでバイナリログをオンにします。

ステップ02)安価なサーバーを使用して、バイナリログを有効にしてMySQL(MDと同じバージョン)をセットアップします。これはDMになります。MDからDMへのレプリケーションをセットアップします。

ステップ03)2番目の安価なサーバーを使用して、バイナリログを無効にしてMySQL(MDと同じバージョン)をセットアップします。--replicate-do-tableを使用するように各監査テーブルを設定します。これはAUになります。DMからAUへのレプリケーションをセットアップします。

ステップ04)mysqldumpでMDからテーブル構造を作成し、それをDMおよびAUにロードします。

ステップ05)MDのすべての監査テーブルを変換してBLACKHOLEストレージエンジンを使用する

ステップ06)DMおよびAUのすべてのテーブルを変換して、BLACKHOLEストレージエンジンを使用する

手順07)MyISAMストレージエンジンを使用するようにAUのすべての監査テーブルを変換する

それが終わったら

  • DMはMDから複製し、バイナリログにのみ記録します
  • --replicate-DO-テーブルのすべての監査テーブルのフィルタ、AUはDMから複製されます

これは、監査情報を別のDBサーバーに格納し、MDが通常持つ書き込みI / Oの低下を減らすことです。


驚異的な答え+++ 1
b_dubb

1

この更新を一括で実行する方法を次に示します。

この例では

  • table_AにはPRIMARY KEY IDがあります
  • idをPRIMARY KEYとしてtable_A_Keys2Updateというテーブルを作成します
  • 更新する必要があることがわかっているtable_AのIDをtable_A_Keys2Updateに入力します

table_A_Keys2Updateを作成するには、次の手順を実行します。

CREATE TABLE table_A_Keys2Update SELECT id FROM table_A;
ALTER TABLE table_A_Keys2Update ADD PRIMARY KEY (id);

リビジョン番号をインクリメントする必要があるIDをtable_A_Keys2Updateに入力した後、次のUPDATE JOINを実行して、IDがtable_Aとtable_A_Keys2Updateの両方にあるすべての行のリビジョン番号をインクリメントします。

UPDATE table_A A INNER JOIN table_A_Keys2Update B USING (id)
SET A.revision = A.revision + 1;

この1行のクエリで、トリガーとストアドプロシージャを置き換えることができます。

必要に応じて、この1つのクエリをストアドプロシージャに配置して、必要に応じて呼び出すことができます。


それは本当に私が興味を持っている挿入です。あなたの場合はINSERT INTO監査SELECT <任意> <primary_table> FROM <パラメータストアドプロシージャから>あなたは一括で挿入を行うことができます。トリガーでは、単にINSERT INTO監査VALUES <更新された行のデータ>を挿入します。では、単一行の行ごとの挿入は、一括挿入よりも高速でしょうか?
リチャード

簡単にするために、トリガーの方がはるかに優れています。1)primary_tableがピーク時間の途中で一括挿入を経験しない場合、2)監査情報はいつでもオンデマンドで読み取る必要があります。3)サイトトラフィックが少ない。
RolandoMySQLDBA
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.