2017-02-08 13 views
1

Я работаю над приложением, которое использует EF6 для большинства операций с базой данных, и по большей части материал базы данных некритический и не находится под большой нагрузкой , Существует одно исключение из этого правила, где мы имеем поток возможных событий 100/s, в которые необходимо вставить или обновить строку в определенной таблице на основе значения столбца.Высокие ошибки параллелизма вызывают взаимоблокировки и время запроса> 30 с

Я слабоват с SQL в целом, но я написал этот запрос, чтобы вставить или обновить и вернуть идентификатор элемента:

DECLARE @Id [int]; 

MERGE {tableName} WITH (UPDLOCK) AS target 
USING (SELECT @MatchName AS id) AS source 
ON source.id = target.MatchColumn 
WHEN MATCHED 
    THEN UPDATE SET @Id = target.Id, ... 
WHEN NOT MATCHED 
    THEN INSERT (...) VALUES (...); 

IF @Id IS NULL 
BEGIN 
    SELECT @Id = CAST(SCOPE_IDENTITY() as [int]); 
END; 
SELECT @Id; 

Это сделано внутри из (EF) сериализуемого блок транзакций, и это единственное, что выполняется в явной транзакции, и является единственным кодом, который обновляет этой таблице. (другие вещи могут читать). Если транзакция откатывается в базе данных (EF генерирует исключение), она повторно повторяется до 3 раз.

Проблема заключается в том, что, когда мы начинаем входить в ситуации с более высокой нагрузкой, мы можем оказаться в этом состоянии, где так много вещей пытается обновить базу данных, а запросы к этой таблице могут начинаться с 30 секунд (запросы к другим таблицам остаются прекрасными). У меня создается впечатление, что, хотя это выполняется в сериализуемой транзакции, он будет блокировать только строки, которые выбираются с помощью слияния, и это должно быть относительно быстрой операцией.

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

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

+0

Было бы хорошо, если выполнить это заявление с некоторыми значениями и приложить фактический план выполнения на вопрос. – Evk

+0

@Evk: Значения представляют собой всего несколько целых/коротких строковых столбцов, могут немного добавить план выполнения. – caesay

ответ

0

По умолчанию merge приобретает updlocks, поэтому with (updlock) ничего не делает для вас. Изменение вашего updlock на holdlock (или serializable) и выполнение оператора в транзакции гарантирует, что правильные блокировки будут получены и удерживаться на время операции.

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


вам нужно использовать set transaction isolation level serializable ли?

В этом случае я не думаю, что была бы какая-либо разница, явно устанавливая уровень изоляции транзакции на serializable, если вышеуказанный код - все, что было бы в транзакции. merge with (holdlock) собирается приобрести блокировки обновлений по умолчанию, которые несовместимы с совместными блокировками, используя подсказку holdlock, решает проблему состояния гонки, как объяснил Дэн Гузман в упомянутой статье и выдержке выше.


with (holdlock) является table hint. Табличные подсказки переопределяют поведение по умолчанию для оператора.

Если в вашей транзакции были и другие операторы, то это будет происходить из-за разницы в уровне изоляции транзакций от уровня изоляции по умолчанию или явно установленного set transaction isolation level (который является уровнем сеанса), если он не переопределен табличной подсказкой.

Самая высокая зернистость выигрывает:

  • низкой: базы данных (по умолчанию read committed)
  • средний: Session (set transaction isolation level ...)
  • высокая: Таблица Hint (with (updlock, serializable))

Больше на Уровни изоляции транзакции:

+0

Итак, какова разница в поведении таблицы между сериализуемой транзакцией без 'holdlock' и, скажем, транзакция с повторяемым чтением с' holdlock'? – caesay

+0

@caesay оба этих сценария = сериализуемые. – SqlZim

+0

@caesay любое обновление? – SqlZim