Во время выполнения запроса SQL Server может приостановить запрос, если сетевые буферы заполнены. Это происходит, если клиент не успевает за чтением сети, т.е. он не вызывает SqlDataReader.Read (). Запрос SQL Server возобновляется после освобождения сетевых буферов, т.е. когда клиент возобновляет SqlDataReader.Read (). Это означает, что пока вы читаете результат набора данных из устройства чтения данных, запрос все еще выполняется на сервере. Есть более подробная информация, такая как размер сетевых буферов, BLOB-операции на стороне клиента с использованием SqlBytes.Stream и другие, но суть идеи заключается в том, что медленный клиент может привести к приостановке запроса и завершению запроса после завершения клиента .
При чтении данных с нормальным уровнем изоляции (чтение передано) SQL Server устанавливает недолговечные общие блокировки на строки, которые он читает. При более высоких уровнях изоляции блокировки являются долгоживущими и удерживаются до конца транзакции.
Если транзакции не используются, каждый оператор SELECT создает неявный переход только для чтения на время действия оператора.
Таким образом, из 1, 2 и 3 видно, что медленный клиент, выполняющий запрос с высоким уровнем изоляции, приведет к длительному удержанию общих блокировок.
Теперь вам нужно детализировать, что вы наблюдаете:
- Какие блокировки удерживаются этим запросом?
- S, U, X?
- строка, страница, таблица?
- замки дальности?
- Происходит ли повышение блокировки?
- Почему блокировки удерживаются на время запроса?
- Используете ли вы уровни изоляции REEATABLE READ или SERIALIZATION? Если да, то почему?
- Используете ли вы подсказки блокировки? Если да, то почему?
Лучшей ставкой, вероятно, будет изоляция моментального снимка (требуется как минимум SQL Server 2005), либо в качестве уровня изоляции моментального снимка, либо как моментальный снимок с фиксацией чтения. Это полностью устранит проблему блокировки, но потенциально создаст некоторое давление ввода-вывода для базы данных tempdb.
Другим решением было бы использование курсора, но он интрузивен на существующей кодовой базе, сложен и все еще подвержен ошибкам (необходимо правильно указать тип курсора).
Кстати, я не рекомендую изменить поведение клиента. Я предполагаю, что прямо сейчас вы выполняете маршалинг бизнес-объектов во время их чтения внутри цикла SqlDataReader.Read, и это способ сделать это. Предварительное чтение в память, а затем маршалинг может добавить больше проблем для больших наборов данных.