У меня относительно небольшой стол (на данный момент). Он работает как причудливая очередь. Задания, выполняющие каждую секунду /, задают эту таблицу для большей работы и всякий раз, когда работа завершается, они сообщают, что эта работа завершена.Как я могу избежать или минимизировать взаимоблокировки в этой ситуации?
Таблица имеет ~ 1000 записей или около того, записи и долгосрочные, мы надеемся, имеют 100k + строки Каждая запись означает задание, которое необходимо выполнить один раз в минуту. Таблица размещена в SQL Azure (S2 plan)
Job Starter выполняет хранимую процедуру, запрашивающую работу из этой таблицы. В принципе, proc просматривает таблицу, видит, какие задачи не выполняются и просрочены, отмечает их как «находящихся в процессе» и возвращает их на стартер задания.
После завершения задачи, простое обновление выполняется, чтобы сказать, что задача завершена, и будет доступна для другого цикла работы в минуту (поле называется контроль частотного)
ПРОБЛЕМА: я получаю тупики тихо часто при запросе этой таблицы для большей работы или попытке отметить завершенные записи. Похоже, подсказка ROWLOCK не работает. Мне нужна структура индексирования в этой таблице?
Вот хранимая процедура, которая извлекает записи (как правило, до 20, в то время, регулируется @count параметром
CREATE PROCEDURE [dbo].[sp_GetScheduledItems]
@activity NVARCHAR (50), @count INT, @timeout INT=300, @dataCenter NVARCHAR (50)
AS
BEGIN
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
DECLARE @batchId uniqueidentifier
SELECT @batchId = NEWID()
DECLARE @result int;
DECLARE @process nvarchar(255);
BEGIN TRAN
-- Update rows
UPDATE Schedule
WITH (ROWLOCK)
SET
LastBatchId = @batchId,
LastStartedProcessingId = NEWID(),
LastStartedProcessingTime = GETUTCDATE()
WHERE
ActivityType = @activity AND
IsEnabled = 1 AND
ItemId IN (
SELECT TOP (@count) ItemId
FROM Schedule
WHERE
(LastStartedProcessingId = LastCompletedProcessingId OR LastCompletedProcessingId IS NULL OR DATEDIFF(SECOND, LastStartedProcessingTime, GETUTCDATE()) > @timeout) AND
IsEnabled = 1 AND ActivityType = @activity AND DataCenter = @dataCenter AND
(LastStartedProcessingTime IS NULL OR DATEDIFF(SECOND, LastStartedProcessingTime, GETUTCDATE()) > Frequency)
ORDER BY (DATEDIFF(SECOND, ISNULL(LastStartedProcessingTime, '1/1/2000'), GETUTCDATE()) - Frequency) DESC
)
COMMIT TRAN
-- Return the updated rows
SELECT ItemId, ParentItemId, ItemName, ParentItemName, DataCenter, LastStartedProcessingId, Frequency, LastProcessTime, ActivityType
FROM Schedule
WHERE LastBatchId = @batchId
END
GO
Вот хранимая процедура, которая помечает элементы как завершенные (он делает это один-на -a-времени)
CREATE PROCEDURE [dbo].[sp_CompleteScheduledItem]
@activity NVARCHAR (50), @itemId UNIQUEIDENTIFIER, @processingId UNIQUEIDENTIFIER, @status NVARCHAR (50), @lastProcessTime DATETIME, @dataCenter NVARCHAR (50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
UPDATE Schedule WITH (ROWLOCK)
SET
LastCompletedProcessingId = LastStartedProcessingId,
LastCompletedProcessingTime = GETUTCDATE(),
LastCompletedStatus = @status,
LastProcessTime = @lastProcessTime
WHERE
ItemId = @itemId AND
LastStartedProcessingId = @processingId AND
DataCenter = @dataCenter AND
ActivityType = @activity
END
GO
Вот сама таблица
CREATE TABLE [dbo].[Schedule](
[ItemId] [uniqueidentifier] NOT NULL,
[ParentItemId] [uniqueidentifier] NOT NULL,
[ActivityType] [nvarchar](50) NOT NULL,
[Frequency] [int] NOT NULL,
[LastBatchId] [uniqueidentifier] NULL,
[LastStartedProcessingId] [uniqueidentifier] NULL,
[LastStartedProcessingTime] [datetime] NULL,
[LastCompletedProcessingId] [uniqueidentifier] NULL,
[LastCompletedProcessingTime] [datetime] NULL,
[LastCompletedStatus] [nvarchar](50) NULL,
[IsEnabled] [bit] NOT NULL,
[LastProcessTime] [datetime] NULL,
[DataCenter] [nvarchar](50) NOT NULL,
[ItemName] [nvarchar](255) NOT NULL,
[ParentItemName] [nvarchar](255) NOT NULL,
CONSTRAINT [PK_Schedule] PRIMARY KEY CLUSTERED
(
[DataCenter] ASC,
[ItemId] ASC,
[ActivityType] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
По крайней мере, три различных соображений. 1. Наличие отсутствия индексов может влиять на поведение ROWLOCK (если есть индекс, индекс может быть заблокирован, а не строка). 2. Уровень изоляции транзакции может повлиять на результат. 3. Возможно, вы захотите использовать UPDLOCK, а не ROWLOCK (искать вокруг, есть ситуации, когда, похоже, это помогает избежать тупиковой ситуации - неожиданно так, насколько мне известно). – DWright