Ответ 1
Вы не можете заставить триггер запускать асинхронно, но вы можете заставить триггер синхронно отправлять сообщение в очередь SQL Service Broker. Затем очередь может обрабатываться асинхронно с помощью хранимой процедуры.
У меня есть триггеры, которые манипулируют и вставляют много данных в таблицу Изменить отслеживание для целей аудита при каждой вставке, обновлении и удалении.
Этот триггер делает свою работу очень хорошо, используя ее, мы можем регистрировать желаемые старые значения/новые значения в соответствии с бизнес-требованиями для каждой транзакции.
Однако в некоторых случаях, когда в исходной таблице много столбцов, транзакция может занять до 30 секунд, что недопустимо.
Есть ли способ запустить триггер асинхронно? Любые примеры.
Вы не можете заставить триггер запускать асинхронно, но вы можете заставить триггер синхронно отправлять сообщение в очередь SQL Service Broker. Затем очередь может обрабатываться асинхронно с помощью хранимой процедуры.
В этих статьях показано, как использовать сервис-брокера для аусинхронного аудита и должно быть полезно:
Существует основной конфликт между "делает свою работу очень хорошо" и "неприемлемой", очевидно.
Мне кажется, что вы пытаетесь использовать триггеры так же, как использовать события в процедуре OO, которое IMHO не отображает.
Я бы назвал любую триггерную логику, которая занимает 30 секунд - нет, более 0,1 секунды - как дисфункциональная. Я думаю, вам действительно нужно перепроектировать вашу функциональность и сделать это по-другому. Я бы сказал, "если вы хотите сделать его асинхронным", но я не думаю, что этот дизайн имеет смысл в любой форме.
Что касается "асинхронных триггеров", основным фундаментальным конфликтом является то, что вы не могли бы включать такую вещь между операторами BEGIN TRAN и COMMIT TRAN, потому что вы потеряли информацию о том, удалось ли это или нет.
Интересно, можете ли вы пометить запись для отслеживания изменений, вставив в таблицу "слишком процесс", включая тех, кто сделал изменение и т.д. и т.д.
Затем может появиться другой процесс и скопировать остальные данные на регулярной основе.
Создайте таблицу истории. Во время обновления (/удаления/вставки) основной таблицы вставьте старые значения записи (удаленная псевдо-таблица в триггере) в таблицу истории; необходима дополнительная информация (временная метка, тип операции, возможно, контекст пользователя). В любом случае, новые значения сохраняются в живой таблице.
Этот способ запускается быстро (er), и вы можете переключать медленные операции на просмотр журнала (процедура).
В 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-сервера.
Не знаю, что я знаю, но вставляете ли вы значения в таблицу аудита, которые также существуют в базовой таблице? Если это так, вы можете рассмотреть возможность отслеживания изменений. Поэтому вставка будет отслеживать время изменения, пользователь, дополнительный и пучок NULL (фактически значение до значения). Обновление будет иметь время изменения, пользователь и т.д. И значение до только измененного столбца. У удаления есть изменение на, и т.д. И все значения.
Кроме того, есть ли таблица аудита для базовой таблицы или одна таблица аудита для БД? Конечно, более поздняя версия более легко может привести к ожиданиям, поскольку каждая транзакция пытается записать в одну таблицу.
Я подозреваю, что ваш триггер состоит из этих генерических триггеров генерации csv/text, предназначенных для регистрации всех изменений для всей таблицы в одном месте. Хорошо в теории (возможно...), но трудно поддерживать и использовать на практике.
Если вы можете запускать асинхронно (что еще потребует хранения данных где-то для регистрации позже), вы не проверяете и не имеете истории для использования.
Возможно, вы можете посмотреть план выполнения триггера и посмотреть, какой бит занимает самый длинный?
Можете ли вы изменить, как вы проверяете, скажем, на таблицу? Вы можете разбить текущие данные журнала на соответствующие таблицы.
Для выполнения асинхронной обработки вы можете использовать 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.