Используете NOLOCK? Вот так вы и получаете неверные результаты запроса

Пересказ статьи Brent Ozar. Using NOLOCK Here’s How You’ll Get the Wrong Query Results.

Волшебный WITH(NOLOCK) в вашем запросе, похоже, заставляет его выполняться быстрее. А как насчет недостатков? Давайте разберемся.

Мы начнем со свободно распространяемой публичной базы данных StackOverflow.com — подойдет любая, даже 10GB mini — и выполним этот запрос:

UPDATE dbo.Users
SET WebsiteUrl = 'https://www.BrentOzar.com/';

Мы просто меняем веб-страницу каждого пользователя на нашу. Затем в отдельном окне, пока выполняется это обновление, выполним:

SELECT COUNT(*) FROM dbo.Users WITH (NOLOCK);
GO 20

Результаты? Картинка полезней тысячи слов:


WITH (NOLOCK, НЕТ_ТОЧНОСТИ)

Конечно, мы запустили обновление на таблице Users, но мы точно не меняем количество пользователей в базе данных. Однако из-за специфики работы NOLOCK мы получаем различное число пользователей всякий раз, когда мы запускаем запрос на выполнение!

Это…это не хорошо. Но это именно так, как задумано. Когда вы используете грязное чтение, также известное как уровень изоляции READ UNCOMMITTED, ваш запрос может давать неверные результаты, в числе которых:

  1. Вы можете видеть строки дважды.
  2. Вы можете вообще пропустить строки.
  3. Вы можете видеть данные, которые не будут зафиксированы.
  4. Ваш запрос может отказать с ошибкой: «невозможно продолжить сканирование без блокировки из-за перемещения данных».

К счастью, это довольно легко пофиксить:

  • Создайте индекс на таблице (любой одностолбцовый индекс будет прекрасно работать в данном конкретном примере, предоставляя SQL Server сокращенную копию таблицы для сканирования).
  • Используйте более подходящий уровень изоляции — скажем, Read Committed Snapshot Isolation.
  • Уберите хинт NOLOCK из запроса — хотя это может закончиться блокировкой, так что вам придется прибегнуть к настройке индексов и запросов.

Добавить комментарий