Поведение DataReader с блокировкой SQL Server - PullRequest
4 голосов
/ 08 октября 2009

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

Так что я предполагаю, что мой слегка наивный вопрос: в какой момент фактически отменяются блокировки, которые снимаются в результате выполнения запроса? Кажется, мы обнаруживаем, что блокировки сохраняются до тех пор, пока не будет обработана последняя строка DataReader, а DataReader фактически не закрыта - кажется ли это правильным? Быстрый 101 о том, как DataReader работает за кулисами, был бы великолепен, так как я изо всех сил пытался найти какую-нибудь приличную информацию о нем.

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

Ответы [ 2 ]

11 голосов
/ 08 октября 2009
  1. Во время выполнения запроса SQL Server может приостановить запрос, если сетевые буферы заполнены. Это происходит, если клиент не успевает за чтением сети, т.е. он не вызывает SqlDataReader.Read (). Запрос SQL Server возобновляется после освобождения сетевых буферов, т.е. когда клиент возобновляет SqlDataReader.Read (). Это означает, что пока вы читаете результат набора данных из устройства чтения данных, запрос все еще выполняется на сервере. Есть более подробная информация, такая как размер сетевых буферов, BLOB-операции на стороне клиента с использованием SqlBytes.Stream и другие, но суть идеи заключается в том, что медленный клиент может привести к приостановке запроса и завершению запроса после завершения клиента .

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

  3. Если транзакции не используются, каждый оператор SELECT создает неявный переход только для чтения на время действия оператора.

Таким образом, из 1, 2 и 3 видно, что медленный клиент, выполняющий запрос с высоким уровнем изоляции, приведет к длительному удержанию общих блокировок.

Теперь вам нужно детализировать, что вы наблюдаете:

  • Какие блокировки удерживаются этим запросом?
    • S, U, X?
    • строка, страница, таблица?
    • замки дальности?
  • Происходит ли повышение блокировки?
  • Почему блокировки удерживаются на время запроса?
    • Используете ли вы уровни изоляции REEATABLE READ или SERIALIZATION? Если да, то почему?
    • Используете ли вы подсказки блокировки? Если да, то почему?

Лучшей ставкой, вероятно, будет изоляция моментального снимка (требуется как минимум SQL Server 2005), либо в качестве уровня изоляции моментального снимка, либо как моментальный снимок с фиксацией чтения. Это полностью устранит проблему блокировки, но потенциально создаст некоторое давление ввода-вывода для базы данных tempdb.

Другим решением было бы использование курсора, но он интрузивен на существующей кодовой базе, сложен и все еще подвержен ошибкам (необходимо правильно указать тип курсора).

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

1 голос
/ 14 июня 2010

выбор во временную таблицу уменьшит продолжительность блокировки

select blah from tbl into #temp << locks held and released

select * from #temp << take all the time you want now
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...