2009-04-20 31 views
30

トリガーは、挿入、更新、削除ごとに多くのデータを操作して、変更トラッキングテーブルに監査目的で挿入します。SQL Server 2005/2008の非同期トリガー

このトリガーは非常にうまく機能します。このトリガーを使用することで、すべてのトランザクションのビジネス要件ごとに希望の古い値/新しい値を記録できます。

ソーステーブルのカラム数が多い場合は、トランザクションが完了するまでに30秒かかることがありますが、これは許容できません。

トリガーを非同期で実行する方法はありますか?すべての例。

+0

変更トラッキングは組み込みの機能です。btw :) https://msdn.microsoft.com/en-us/library/bb933874.aspx – moander

答えて

23

トリガーを非同期に実行させることはできませんが、トリガーがメッセージをSQL Service Brokerキューに同期送信することができます。キューは、ストアドプロシージャによって非同期に処理できます。

+0

しかし、あなたはまだトランザクション制御を破っています。 – dkretz

+1

Service Brokerを本当に理解している人が、上記のコメント(「トランザクションの中断」)が真であるかどうかを説明できますか? –

+2

トランザクションをコミットまたはロールバックするには、すべてが成功した(コミットする)か、何かが失敗した(ロールバックする)まで待つ必要があります。非同期とは、残りのロジックを続行するのを待たずに完了することを意味します。 – dkretz

0

これはわかりませんが、基本テーブルにも存在するAuditテーブルに値を挿入していますか?もしそうならば、変更だけを追跡することを検討することができます。したがって、挿入は、変更時間、ユーザ、余分なもの、および(実際には前の値である)NULLの束を追跡するでしょう。更新は、変更された列の変更時刻、ユーザーなど、および前の値のみを持ちます。削除は、などの値とすべての値に変更があります。

また、ベーステーブルごとに監査テーブルがあるのか​​、そのDBに1つの監査テーブルがありますか?もちろん、各トランザクションが1つのテーブルに書き込もうとすると、後でより簡単に待機することができます。

1

私はあなたが

などなどの変更をした者を含む「あまりにプロセス」テーブルに挿入することにより、変更の追跡のための記録にタグを付けることができれば、その後、別のプロセスに沿って来て、残りのデータをコピーすることができ疑問定期的に。

2

明らかに「仕事が非常にうまくいく」と「容認できない」の間に基本的な矛盾があります。

IMOがマップしていないOO手続き型アプリケーションでイベントを使用するのと同じ方法で、トリガーを使用しようとしていることは間違いありません。

30秒(いいえ、0.1秒以上)がかかるトリガーロジックは、機能不全と呼ばれます。私は実際にあなたの機能を再設計し、他の方法でやる必要があると思います。私は「非同期にしたいのなら」と言っていますが、このデザインはどんな形でも意味をなさないと思います。

「非同期トリガー」に関する限り、基本的な基本的な矛盾は、BEGIN TRANとCOMMIT TRANステートメントの間にそのようなものを含めることはできないということです。

+0

上記のコメントは、Service Brokerを使用すると「依然としてトランザクション制御を破っています。私はService Brokerを使用していませんが、トランザクションではありませんか? –

+0

非同期の場合はできませんでした。成功したかどうかを知るために完了するのを待たずに、トランザクションをコミットするかロールを起こすかを知るために、トランザクション内に保持することはできません。 – dkretz

+0

この会話を2つのコメント・トレールの間で分割することをお詫び申し上げます。 私は、SBがそのキューからロールバックを許可できるのであれば、それはトランザクションになると思います。キューに書き込んだら成功したとみなします。 良い点がありますが、それは設計/定義上の問題(SBキューへの書き込みをトランザクション全体の一部としてロールバックできると仮定して)が多いようです。 –

0

あなたのトリガーは、これらの一般的なcsv/text生成トリガーで、すべてのテーブルのすべての変更を1か所に記録するように設計されていると思われます。理論的には良いですが(おそらく...)、実際には維持して使用するのが難しいです。

非同期で実行することができます(これは、後で再度ログに記録するためにデータを格納する必要がある場合)。監査は行われておらず、使用する履歴もありません。

おそらく、トリガーの実行計画を見て、どのビットが最も長い時間を費やしているのか見ることができますか?

テーブル単位で監査する方法を変更できますか?現在のログデータを関連するテーブルに分割することができます。

1

履歴テーブルを作成します。メインテーブルを更新(/削除/挿入)している間に、履歴テーブルにレコードの古い値(削除された擬似テーブルがトリガ)を挿入します。いくつかの追加情報も必要です(タイムスタンプ、操作タイプ、多分ユーザーコンテキスト)。新しい値はライブテーブルに保持されます。

このようにすると、高速で実行され、遅い操作をログビューアに移行することができます(手順)。

2

のSQL Server 2014はDelayed Durabilityと呼ばれる非常に興味深い機能を導入しました。サーバークラッシュのような致命的なイベントが発生した場合にいくつかの行を失うことを許すことができれば、あなたのようなシナリオではパフォーマンスを大幅に向上させることができます。

遅延トランザクション耐久性は、非同期ログ のディスクへの書き込みを使用して実行されます。トランザクションログレコードはバッファに保存され、バッファがいっぱいになったりバッファフラッシングイベントが発生すると、 はディスクに書き込まれ、 になります。遅延トランザクションの耐久性により、システム内の競合と遅延が軽減されます。

遅延耐久性を考慮して、テーブルを含むデータベースを変更する必要があります。

ALTER DATABASE dbname SET DELAYED_DURABILITY = ALLOWED 

次に、トランザクションごとに耐久性を制御できます。トランザクションはクロスデータベースですので、あなたの監査テーブルがトリガーと同じデータベース内に配置されている場合にのみ動作する場合

begin tran 

insert into ChangeTrackingTable select * from inserted 

commit with(DELAYED_DURABILITY=ON) 

トランザクションは、耐久性としてコミットされます。

また、データベースを許可の代わりに強制的に変更する可能性もあります。これにより、データベース内のすべてのトランザクションが遅延するようになります。遅延耐久性のため

ALTER DATABASE dbname SET DELAYED_DURABILITY = FORCED 

は、予期しないシャットダウン とSQL Serverの予想シャットダウン/再起動の間に違いはありません。 致命的なイベントのように、データ損失を計画する必要があります。計画中の シャットダウン/再起動では、ディスクに書き込まれていないトランザクションの一部が最初にディスクに保存されることがありますが、計画しないでください。プランニングされているかどうかに関係なく、シャットダウン/再起動は、致命的なイベントと同じデータを失いますが、 として計画してください。

この奇妙な欠陥がうまくいけば、将来のリリースで解決されますが、それまでには、SQLサーバーが再起動やシャットダウン時に自動的に「sp_flush_log」プロシージャを実行することを確認するのが賢明かもしれません。

0

非同期処理を実行するには、Service Brokerを使用できますが、唯一のオプションではありません.CLRオブジェクトも使用できます。呼び出しの

CREATE PROCEDURE SyncProcedure(@data xml) 
AS 
    INSERT INTO T(Data) VALUES (@data) 

例:それが呼び出すことSyncProcedure非同期デリゲートを使用

using System; 
using System.Data; 
using System.Data.SqlClient; 
using System.Data.SqlTypes; 
using Microsoft.SqlServer.Server; 
using System.Runtime.Remoting.Messaging; 
using System.Diagnostics; 

public delegate void AsyncMethodCaller(string data, string server, string dbName); 

public partial class StoredProcedures 
{ 
    [Microsoft.SqlServer.Server.SqlProcedure] 
    public static void AsyncProcedure(SqlXml data) 
    { 
     AsyncMethodCaller methodCaller = new AsyncMethodCaller(ExecuteAsync); 
     string server = null; 
     string dbName = null; 
     using (SqlConnection cn = new SqlConnection("context connection=true")) 
     using (SqlCommand cmd = new SqlCommand("SELECT @@SERVERNAME AS [Server], DB_NAME() AS DbName", cn)) 
     { 
      cn.Open(); 
      using (SqlDataReader reader = cmd.ExecuteReader()) 
      { 
       reader.Read(); 
       server = reader.GetString(0); 
       dbName = reader.GetString(1); 
      } 
     } 
     methodCaller.BeginInvoke(data.Value, server, dbName, new AsyncCallback(Callback), null); 
     //methodCaller.BeginInvoke(data.Value, server, dbName, null, null); 
    } 

    private static void ExecuteAsync(string data, string server, string dbName) 
    { 
     string connectionString = string.Format("Data Source={0};Initial Catalog={1};Integrated Security=SSPI", server, dbName); 
     using (SqlConnection cn = new SqlConnection(connectionString)) 
     using (SqlCommand cmd = new SqlCommand("SyncProcedure", cn)) 
     { 
      cmd.CommandType = CommandType.StoredProcedure; 
      cmd.Parameters.Add("@data", SqlDbType.Xml).Value = data; 
      cn.Open(); 
      cmd.ExecuteNonQuery(); 
     } 
    } 

    private static void Callback(IAsyncResult ar) 
    { 
     AsyncResult result = (AsyncResult)ar; 
     AsyncMethodCaller caller = (AsyncMethodCaller)result.AsyncDelegate; 
     try 
     { 
      caller.EndInvoke(ar); 
     } 
     catch (Exception ex) 
     { 
      // handle the exception 
      //Debug.WriteLine(ex.ToString()); 
     } 
    } 
} 

は、次その非同期の別の手順(SyncProcedure)を呼び出し、ストアドプロシージャ(AsyncProcedure)の一例ですAsyncProcedure:

EXEC dbo.AsyncProcedure N'<doc><id>1</id></doc>' 

不幸にも、アセンブリにはUNSAFEの許可が必要です。