2009-04-20 148 views
30

我有觸發器可以操縱並插入大量數據到更改跟蹤表用於每次插入,更新和刪除時的審計目的。SQL Server 2005/2008中的異步觸發器

這個觸發器很好地完成了它的工作,通過使用它,我們可以根據每個事務的業務需求記錄所需的舊值/新值。

但是,在一些源表中有很多列的情況下,交易完成可能需要長達30秒,這是不可接受的。

有沒有辦法讓觸發器異步運行?任何示例。

+0

更改跟蹤是一個內置的功能btw :) https://msdn.microsoft.com/en-us/library/bb933874.aspx – moander 2015-03-21 06:19:49

回答

23

您不能讓觸發器異步運行,但您可以讓觸發器同步向SQL Service Broker隊列發送消息。隊列可以通過存儲過程異步處理。

0

不是我所知道的,而是將值插入基表中也存在的審覈表中?如果是這樣,你可以考慮跟蹤更改。因此,插入會跟蹤更改時間,用戶,額外和一堆NULL(實際上是前值)。更新將只有更改時間,用戶等和更改列的前值。刪除在等等和所有值都有變化。

另外,您是否有每個基表的審計表或數據庫的審計表?當然,隨着每個事務嘗試寫入一個表,後者可能更容易導致等待。

1

我不知道你可以通過插入包括誰做的改變等等等等

然後另一個進程可能會出現並複製數據的其餘部分「太工藝」 table標籤的更改跟蹤記錄定期進行。

2

顯然,「很好地工作」和「不可接受的」之間存在基本的衝突。

這聽起來就像你試圖使用觸發器一樣,你會使用OO程序應用程序中的事件,恕我直言沒有映射。

我會調用任何觸發邏輯,需要30秒 - 不,更多0.1秒 - 作爲失效。我認爲你真的需要重新設計你的功能,並以其他方式做。我會說「如果你想使它異步」,但我不認爲這種設計在任何形式上都有意義。

就「異步觸發器」而言,基本的基本衝突是您不能在BEGIN TRAN和COMMIT TRAN語句之間包含這樣的事物,因爲您已經失去了跟蹤成功與否的信息。

+0

你上面評論說,使用Service Broker是「仍在破壞事務控制。」我沒有使用過Service Broker,但是它不是事務性的嗎? – 2009-04-20 17:42:17

+0

它不可能是異步的。如果您不等待它完成以確定它是否成功,就知道是否提交或滾動確認,那麼它不能在交易中保留。 – dkretz 2009-04-20 22:10:14

0

我懷疑你的觸發器是這些通用的csv /文本生成觸發器,用於在一個地方記錄所有表的所有更改。理論上好(也許......),但在實踐中難以維護和使用。

如果您可以異步運行(這仍然需要將數據存儲在某個地方以供日後再次登錄),那麼您不是審計,也沒有歷史記錄可供使用。

也許你可以看看觸發器的執行計劃,看看哪個位最長?

你能改變你對每張表的審計方式嗎?您可以將當前的日誌數據分割到相關的表格中。

1

創建歷史表。在更新(/刪除/插入)主表時,將舊的記錄值(觸發器中刪除的僞表)插入歷史表中;還需要一些額外的信息(時間戳,操作類型,可能是用戶上下文)。無論如何,新值都保存在實時表中。

這種方式可以快速運行(er),您可以將慢速操作轉移到日誌查看器(過程)。

2

的SQL Server 2014引入了一個非常有趣的功能叫做Delayed Durability。如果在發生災難性事件時(例如服務器崩潰),您可以容忍丟失幾行數據,那麼您可以真正提升您的schenarios中的性能。

延遲的事務持久性是使用異步日誌 寫入磁盤來完成的。事務日誌記錄保存在緩衝區中,並在緩衝區填充或緩衝區刷新事件佔用 位置時寫入磁盤。延遲事務耐久性降低了系統

包含表的數據庫必須首先被改變,以允許延遲耐久性內延遲和 爭用。

ALTER DATABASE dbname SET DELAYED_DURABILITY = ALLOWED 

然後,您可以控制每筆交易的持久性。

begin tran 

insert into ChangeTrackingTable select * from inserted 

commit with(DELAYED_DURABILITY=ON) 

的交易將COMMITED耐用,如果交易是跨數據庫,因此,如果您的審覈表位於同一個數據庫觸發器這隻會工作。

也有可能將數據庫更改爲強制而非允許。這會導致數據庫中的所有事務延遲持久。

ALTER DATABASE dbname SET DELAYED_DURABILITY = FORCED 

對於延遲的耐久性,就有意想不到的 關機和SQL Server的預期關機/重啓之間沒有什麼區別。像 災難性事件,您應該計劃數據丟失。在計劃的 關機/重新啓動時,一些尚未寫入磁盤 的事務可能首先保存到磁盤,但不應對此進行規劃。計劃爲 儘管關閉/重新啓動(無論是計劃還是未計劃),數據丟失與災難性事件相同。

這種奇怪的缺陷有望在將來的版本中得到解決,但在那之前它可能是明智的,以確保自動執行SQL服務器重新啓動或關閉時,「sp_flush_log」過程。

0

要執行異步處理,您可以使用Service Broker,但它不是唯一選項,您還可以使用CLR對象。

下面是一個存儲過程(AsyncProcedure)的示例異步調用另一個程序(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:調用的

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

例AsyncProcedure:

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

不幸的是,該程序集需要UNSAFE許可。