quinta-feira, abril 05, 2007

Concorrência na inserção e remoção em SQL

Imagine-se que se tem uma tabela que contem umas centenas de milhares de tuplos. Diariamente existe um job que se encarrega de apagar todos os tuplos, mas durante o período de remoção faz lock à tabela o que impossibilita inserções. O código típico para esta situação é o seguinte:

DECLARE @IDMax dbo.UDT_ID;
SELECT TOP 1 @IDMax = [ID]
FROM [dbo].[TABELA_MILHARES_DE_TUPLOS]
ORDER BY [ID] DESC
IF @IDMax is null
RETURN;
DELETE FROM [dbo].[TABELA_MILHARES_DE_TUPLOS]
WHERE ID <= @IDMax
Neste cenário a inserção também tem de ser rápida pois o timeout definido é de curta duração. O objectivo é continuar a existir a remoção, mas tem de se conseguir fazer inserções mesmo quando a remoção está a ser efectuada. O código seguinte mostra uma possível solução:
DECLARE @IDMax dbo.UDT_ID;
DECLARE @Counter dbo.UDT_ID;

SELECT TOP 1 @IDMax = [ID]
FROM [dbo].[TABELA_MILHARES_DE_TUPLOS]
ORDER BY [ID] DESC
IF @IDMax is null
RETURN;
SET @Counter = 1;
WHILE (@Counter > 0)
BEGIN
DELETE FROM [dbo].[TABELA_MILHARES_DE_TUPLOS]
WITH (ROWLOCK)
WHERE ID =
(
SELECT TOP 1 [ID]
FROM [dbo].[TABELA_MILHARES_DE_TUPLOS]
WITH (NOLOCK)
WHERE [ID] <= @IDMax ORDER BY [ID] DESC
)
SET @Counter = @@ROWCOUNT;
END
Esta solução possibilita que o lock seja feito linha a linha e não à tabela, logo é possível inserir enquanto a remoção está a ser feita. Agora temos de ter a noção que esta solução não é perfeita apesar de servir para esta situação. O tempo de remoção de todas as linhas posso dizer que aumentou 8 vezes, mas como neste caso este é um job que vai ser executado 1 vez por dia, o tempo não é assim tão problemático e chegamos ao objectivo de haver inserções enquanto estão a ser executadas as remoções.

Sem comentários: