簡體   English   中英

SQL Server 2005/2008 中的異步觸發器

[英]Asynchronous Triggers in SQL Server 2005/2008

我有觸發器,可以在每次插入、更新和刪除時操作並將大量數據插入到更改跟蹤表中,以便進行審計。

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

但是,在某些情況下,源表有很多列,事務可能需要 30 秒才能完成,這是不可接受的。

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

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

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”過程可能是明智的。

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

以下是異步調用另一個過程 (SyncProcedure) 的存儲過程 (AsyncProcedure) 示例:

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 許可。

從 sql server 2008 開始,您可以使用CDC功能自動記錄更改,這完全是異步的。 此處查找更多詳細信息

我想知道您是否可以通過插入“太進程”表(包括誰進行了更改等)來標記更改跟蹤的記錄。

然后另一個進程可能會出現並定期復制其余數據。

顯然,“做得很好”和“不可接受”之間存在基本沖突。

在我看來,您正在嘗試以與在 OO 程序應用程序中使用事件相同的方式使用觸發器,恕我直言,它沒有映射。

我會稱任何需要 30 秒的觸發邏輯 - 不,超過 0.1 秒 - 都無法正常工作。 我認為您確實需要重新設計您的功能並以其他方式進行。 我會說“如果你想讓它異步”,但我認為這種設計在任何形式上都沒有意義。

至於“異步觸發器”,基本的根本沖突是您永遠不能在 BEGIN TRAN 和 COMMIT TRAN 語句之間包含這樣的東西,因為您已經忘記了它是否成功。

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

這種方式觸發器運行得很快(呃),您可以將慢速操作轉移到日志查看器(過程)。

不是我所知道的,但是您是否將值插入也存在於基表中的審計表中? 如果是這樣,您可以考慮僅跟蹤更改。 因此,插入將跟蹤更改時間、用戶、額外和一堆 NULL(實際上是之前的值)。 更新將僅具有更改時間、用戶等和更改列的之前值。 刪除具有更改 at 等和所有值。

另外,對於每個基表,您是否有一個審計表或一個數據庫審計表? 當然,后者更容易導致等待,因為每個事務都試圖寫入一個表。

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

如果您可以異步運行(這仍然需要將數據存儲在某處以便稍后再次記錄),那么您就沒有進行審計,也沒有可以使用的歷史記錄。

也許您可以查看觸發器執行計划,看看哪個位花費的時間最長?

你能改變你的審計方式嗎,比如,每張表? 您可以將當前日志數據拆分到相關表中。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM