Снимок включен, все еще тупики, ROWLOCK - PullRequest
5 голосов
/ 16 сентября 2009

Я включил изоляцию моментальных снимков в своей базе данных, используя следующий код

ALTER DATABASE MyDatabase
SET ALLOW_SNAPSHOT_ISOLATION ON

ALTER DATABASE MyDatabase
SET READ_COMMITTED_SNAPSHOT ON

и избавился от множества тупиков.

Но моя база данных по-прежнему создает взаимоблокировки, когда мне нужно запускать скрипт каждый час для очистки более 100 000 строк.

  • Есть ли способ избежать взаимоблокировок в моем сценарии очистки, нужно ли специально устанавливать ROWLOCK в этом запросе?
  • Есть ли способ увеличить количество блокировок на уровне строк, которые использует база данных?
  • Как продвигаются блокировки? От уровня строки до уровня страницы до уровня таблицы?

Мой скрипт удаления довольно прост:

delete statvalue
from statValue,
(select  dateadd(minute,-60, getdate()) as cutoff_date) cd
where temporaryStat = 1
and entrydate < cutoff_date

Сейчас я ищу быстрое решение, но долгосрочное решение было бы еще лучше.

Большое спасибо, Patrikc

Ответы [ 3 ]

6 голосов
/ 16 сентября 2009

Изоляция SNAPSHOT может только уменьшить (некоторые) взаимные блокировки, связанные с чтением, но она абсолютно ничего не делает, чтобы избежать взаимных блокировок записи и записи. Если вы генерируете более 100 000 строк в час, что составляет ~ 30 операций вставки в секунду, сканирование при удалении почти наверняка конфликтует с другими операциями записи. Если все, что вы делаете, это Вставка, никогда не обновлять, тогда блок удаления, но не тупик при блокировке на уровне строки, но так как таблица достаточно велика и удаление выполняет сканирование, движок, скорее всего, выберет блокировку страницы для удаления, следовательно, вероятно, тупик, который вы получите.

без индекса на входной дате, при удалении которого нет выбора, кроме как сканировать всю таблицу. Такого рода таблицы, которые часто вставляются вверху и удаляются внизу, на самом деле являются очередями, и вы должны организовать их по дате входа. Это означает, что entrydate , вероятно, должен быть крайним левым ключом в кластерном индексе . Эта организация допускает четкое разделение вставок, встречающихся на одном конце таблицы, и удалений, встречающихся на другом конце. Но это довольно радикальное изменение, особенно если вы используете statvalueid для чтения этих значений. Я предполагаю, что сейчас у вас есть кластеризованный индекс, основанный на поле автоинкремента (StatValueId). Также я предполагаю, что entrydate и statvalueid взаимосвязаны. Если оба предположения верны, то вы должны удалить базу don statvalueid: найти самый большой идентификатор, который можно удалить, а затем удалить все в кластерном индексе слева от этого идентификатора:

declare @statvalueidmax int;
select @statvalueidmax = max(statvalueid) 
 from statvalue with (readpast)
 where entrydate <  dateadd(minute,-60, getdate());

delete statvalue
 where statvalueid <= @statvalueidmax;

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

0 голосов
/ 16 сентября 2009

Пожалуйста, напишите ваш запрос на удаление следующим образом:

DECLARE @cutoff_date DATETIME

SET @cutoff_date = dateadd(minute,-60, getdate())

delete statvalue
from statValue
where temporaryStat = 1
and entrydate < @cutoff_date

Вы увидите сокращение затрат в плане выполнения

0 голосов
/ 16 сентября 2009

Способ сделать это, чтобы уменьшить (или избежать) взаимоблокировки, состоит в удалении в пакетах с коротким ожиданием между каждым пакетом (используя WAITFOR DELAY)

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

Этот код требует несколько деклараций, и это только для примера (Выполнить на свой страх и риск!).

SELECT  @intRowCount = 1,
    @intErrNo = 0

DECLARE @cutoff_date DATETIME

SET @cutoff_date = dateadd(minute,-60, getdate())

SELECT  @intRowsToDelete = COUNT(*) -- Number of rows to be deleted
FROM dbo.statValue 
WHERE temporaryStat = 1
AND  entrydate < @cutoff_date

WHILE @intRowCount > 0 AND @intErrNo = 0
BEGIN

    SET ROWCOUNT @DEL_ROWCOUNT

    delete statvalue
    FROM dbo.statValue 
    WHERE temporaryStat = 1
    AND  entrydate < @cutoff_date

    SELECT  @intErrNo = @@ERROR, @intRowCount = @@ROWCOUNT

    SET ROWCOUNT 0  -- Reset batch size to "all"

    SELECT  @intRowsToDelete = @intRowsToDelete - @intRowCount

    WAITFOR DELAY '000:00:
 END

Я также включил предложение Джона о неоднократном расчете критериев диапазона дат.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...