Подтвердить что ты не робот

Асинхронные триггеры в SQL Server 2005/2008

У меня есть триггеры, которые манипулируют и вставляют много данных в таблицу Изменить отслеживание для целей аудита при каждой вставке, обновлении и удалении.

Этот триггер делает свою работу очень хорошо, используя ее, мы можем регистрировать желаемые старые значения/новые значения в соответствии с бизнес-требованиями для каждой транзакции.

Однако в некоторых случаях, когда в исходной таблице много столбцов, транзакция может занять до 30 секунд, что недопустимо.

Есть ли способ запустить триггер асинхронно? Любые примеры.

4b9b3361

Ответ 1

Вы не можете заставить триггер запускать асинхронно, но вы можете заставить триггер синхронно отправлять сообщение в очередь SQL Service Broker. Затем очередь может обрабатываться асинхронно с помощью хранимой процедуры.

Ответ 3

Существует основной конфликт между "делает свою работу очень хорошо" и "неприемлемой", очевидно.

Мне кажется, что вы пытаетесь использовать триггеры так же, как использовать события в процедуре OO, которое IMHO не отображает.

Я бы назвал любую триггерную логику, которая занимает 30 секунд - нет, более 0,1 секунды - как дисфункциональная. Я думаю, вам действительно нужно перепроектировать вашу функциональность и сделать это по-другому. Я бы сказал, "если вы хотите сделать его асинхронным", но я не думаю, что этот дизайн имеет смысл в любой форме.

Что касается "асинхронных триггеров", основным фундаментальным конфликтом является то, что вы не могли бы включать такую ​​вещь между операторами BEGIN TRAN и COMMIT TRAN, потому что вы потеряли информацию о том, удалось ли это или нет.

Ответ 4

Интересно, можете ли вы пометить запись для отслеживания изменений, вставив в таблицу "слишком процесс", включая тех, кто сделал изменение и т.д. и т.д.

Затем может появиться другой процесс и скопировать остальные данные на регулярной основе.

Ответ 5

Создайте таблицу истории. Во время обновления (/удаления/вставки) основной таблицы вставьте старые значения записи (удаленная псевдо-таблица в триггере) в таблицу истории; необходима дополнительная информация (временная метка, тип операции, возможно, контекст пользователя). В любом случае, новые значения сохраняются в живой таблице.

Этот способ запускается быстро (er), и вы можете переключать медленные операции на просмотр журнала (процедура).

Ответ 6

В 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. подобно катастрофические события, вы должны планировать потерю данных. В запланированном выключение/перезагрузка некоторых транзакций, которые не были записаны на диск сначала можно сохранить на диск, но вы не должны планировать его. Планируйте как хотя выключение/перезапуск, будь то запланированный или незапланированный, теряет данные аналогичны катастрофическим событиям.

Этот странный дефект, мы надеемся, будет рассмотрен в будущей версии, но до тех пор было бы разумным, чтобы автоматически выполнять процедуру sp_flush_log при перезапуске или завершении SQL-сервера.

Ответ 7

Не знаю, что я знаю, но вставляете ли вы значения в таблицу аудита, которые также существуют в базовой таблице? Если это так, вы можете рассмотреть возможность отслеживания изменений. Поэтому вставка будет отслеживать время изменения, пользователь, дополнительный и пучок NULL (фактически значение до значения). Обновление будет иметь время изменения, пользователь и т.д. И значение до только измененного столбца. У удаления есть изменение на, и т.д. И все значения.

Кроме того, есть ли таблица аудита для базовой таблицы или одна таблица аудита для БД? Конечно, более поздняя версия более легко может привести к ожиданиям, поскольку каждая транзакция пытается записать в одну таблицу.

Ответ 8

Я подозреваю, что ваш триггер состоит из этих генерических триггеров генерации csv/text, предназначенных для регистрации всех изменений для всей таблицы в одном месте. Хорошо в теории (возможно...), но трудно поддерживать и использовать на практике.

Если вы можете запускать асинхронно (что еще потребует хранения данных где-то для регистрации позже), вы не проверяете и не имеете истории для использования.

Возможно, вы можете посмотреть план выполнения триггера и посмотреть, какой бит занимает самый длинный?

Можете ли вы изменить, как вы проверяете, скажем, на таблицу? Вы можете разбить текущие данные журнала на соответствующие таблицы.

Ответ 9

Для выполнения асинхронной обработки вы можете использовать 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.