Мы используем базу данных SQL Server 2005 (без управления версиями строк) с огромным предложением select, и мы видим, что он блокирует запуск других операторов (см. sp_who2
). Я не понимал, что инструкции SELECT могут вызвать блокировку - есть ли что-нибудь, что я могу сделать для смягчения этого?
Операторы SELECT SQL Server, вызывающие блокировку
Ответ 1
SELECT может блокировать обновления. Правильно спроектированная модель данных и запрос вызовут минимальную блокировку и не будут проблемой. "Обычный" с подсказкой NOLOCK - почти всегда неправильный ответ. Правильный ответ - настроить ваш запрос, чтобы он не просматривал огромные таблицы.
Если запрос не поддаётся проверке, вы должны сначала рассмотреть уровень SNAPSHOT ISOLATION, во-вторых, вы должны рассмотреть возможность использования DATABASE SNAPSHOTS, а последний вариант должен быть DIRTY READS (и лучше изменить уровень изоляции , а не использовать СОВЕТ NOLOCK). Обратите внимание, что грязные чтения, как ясно указано в названии, возвращают несогласованные данные (например, ваш общий лист может быть несбалансированным).
Ответ 2
Из документация:
Блокировки
Shared (S)
позволяют параллельным транзакциям читать(SELECT)
ресурс под пессимистичным контролем concurrency. Для получения дополнительной информации см.Types of Concurrency Control
. Никакие другие транзакции не могут изменять данные, а блокировкиShared (S)
существуют на ресурсе.Shared (S)
блокировки на ресурсе освобождаются, как только завершается операция чтения, если уровень изоляции транзакции не установлен на повторяемое чтение или выше, или указатель блокировки используется для сохранения блокировокShared (S)
на время транзакции.
A shared lock
совместим с другой общей блокировкой или блокировкой обновления, но не с исключительной блокировкой.
Это означает, что ваши запросы SELECT
будут блокировать запросы UPDATE
и INSERT
и наоборот.
Запрос A SELECT
поместит временную общую блокировку, когда он прочитает блок значений из таблицы и удалит его, когда он сделает чтение.
За время существования блокировки вы не сможете ничего сделать с данными в заблокированной области.
Два запроса SELECT
никогда не будут блокировать друг друга (если они не являются SELECT FOR UPDATE
)
Вы можете включить уровень изоляции SNAPSHOT
в своей базе данных и использовать его, но обратите внимание, что это не помешает UPDATE
запросам блокироваться запросами SELECT
(что похоже на ваш случай).
Это, тем не менее, предотвратит блокировку SELECT
запросов UPDATE
.
Также обратите внимание, что SQL Server
, в отличие от Oracle
, использует диспетчер блокировок и блокирует его в связанном в памяти списке.
Это означает, что при большой нагрузке сам факт размещения и удаления блокировки может быть медленным, поскольку связанный список сам должен быть заблокирован потоком транзакций.
Ответ 3
Чтобы выполнить грязные чтения, вы можете:
using (new TransactionScope(TransactionScopeOption.Required,
new TransactionOptions {
IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
{
//Your code here
}
или
SelectCommand = "SELECT * FROM Table1 WITH (NOLOCK) INNER JOIN Table2 WITH (NOLOCK) ..."
помните, что вы должны писать WITH (NOLOCK) после того, как каждая таблица, которую вы хотите прочитать,
Ответ 4
Вы можете установить уровень транзакции для чтения незарегистрированных
Ответ 5
Вы также можете получить взаимоблокировки:
"взаимоблокировки с участием только одной таблицы" http://sqlblog.com/blogs/alexander_kuznetsov/archive/2009/01/01/reproducing-deadlocks-involving-only-one-table.aspx
и/или неверные результаты:
"Выбирается под READ COMMITTED и REPEATABLE READ может возвращать неправильные результаты."
Ответ 6
Вы можете использовать WITH(READPAST)
подсказку WITH(READPAST)
. Это отличается от WITH(NOLOCK)
. Он получит данные до начала транзакции и никого не заблокирует. Представьте, что вы запустили оператор до начала транзакции.
SELECT * FROM table1 WITH (READPAST)