Является ли READ UNCOMMITTED / NOLOCK безопасным в этой ситуации?

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

У меня есть таблица, которая выглядит примерно так:

drop table Data

create table Data
(
    Id BIGINT NOT NULL,
    Date BIGINT NOT NULL,
    Value BIGINT,
    constraint Cx primary key (Date, Id)
)

create nonclustered index Ix on Data (Id, Date)

Там нет обновлений в таблице, никогда. Удаление может происходить, но они никогда не должны конкурировать с SELECT, потому что они влияют на другой, более старый конец таблицы. Вставки являются обычными, а разбиение страницы на индекс (Id, Date) встречается крайне часто.

У меня тупиковая ситуация между стандартным INSERT и SELECT, которая выглядит следующим образом:

select top 1 Date, Value from Data where Id = @p0 order by Date desc

потому что INSERT получает блокировку для Cx (Date, Id; Value) и затем Ix (Id, Date), но SELECT получает блокировку для Ix (Id, Date) и затем Cx (Date, Id; Value). Это потому, что SELECT сначала ищет на Ix, а затем присоединяется к поиску на Cx.

Замена кластеризованного и некластеризованного индекса нарушит этот цикл, но это неприемлемое решение, поскольку оно будет вводить циклы с другими (более сложными) SELECT.

Если я добавлю NOLOCK в SELECT, может ли он пойти не так в этом случае? Может ли это вернуться:

  1. Более одного ряда, хотя я просил ТОП 1?
  2. Нет строк, хотя один существует и был зафиксирован?
  3. Хуже всего то, что строка не удовлетворяет предложению WHERE?

Я много читал об этом в Интернете, но единственные репродукции аномалий избыточного или недостаточного количества, которые я видел ( один, два), включают сканирование. Это включает только поиск. Джефф Этвуд написал статью об использовании NOLOCK, которая вызвала хорошую дискуссию. Меня особенно заинтересовал комментарий Рика Таунсенда:

Во-вторых, если вы читаете грязные данные, вы рискуете прочитать совершенно неверную строку. Например, если ваш выбор считывает индекс, чтобы найти вашу строку, то обновление изменяет расположение строк (например, из-за разбиения страницы или обновления кластеризованного индекса), когда ваш выбор идет, чтобы прочитать фактическую строку данных или его там больше нет, или совсем другой ряд!

Это возможно только со вставками и без обновлений? Если так, то я думаю, что даже мои поиски в таблице только для вставки могут быть опасными.


Обновить:

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

Но в моем случае ни в одной строке не будет более одной версии, поэтому хранилище версий кажется довольно бессмысленным. И если строка была найдена без общей блокировки, чем она отличается от использования NOLOCK?

2 ответа

Решение

Использование NOLOCK или READ UNCOMMITTED означает, что вы отказываетесь от какой-либо гарантии согласованности. Период.

Если вам нужна последовательность, не делайте грязного чтения. Все ваше объяснение основано на недокументированном поведении, которое может измениться в будущих выпусках, и, что еще хуже, на конкретных планах доступа, которые вы ожидаете для своего запроса. Оптимизатор запросов может свободно выбирать любой план, который сочтет нужным, и любые предположения, которые вы делаете, могут быть сорваны при производстве. Итак, вернемся к исходной точке: не делайте грязных чтений, если вы не готовы столкнуться с последствиями.

Не уверен, что это применимо, не ясно, чего вы пытаетесь достичь с помощью вашего запроса / таблицы, но, возможно, эта статья может помочь: Использование таблиц в качестве очередей.

обновленный
Там, где чтение NOLOCK считывает несогласованное состояние (например, читает устаревший некластеризованный индексный ключ и преследует его до отсутствующей строки в кластеризованном индексе), при считывании моментального снимка будет найдена "пропущенная" строка в хранилище версий. Для стабильных данных чтение снимка идентично чтению nolock. Волшебство хранилища версий вступает в игру всякий раз, когда данные изменяются (незафиксированные обновления), потому что считанные снимки попадают в хранилище версий и находят "старые" значения (стабильные и согласованные), при которых чтение nolock могло бы привести к сбою и указателям погони в Лала Лэнд.

В этом случае вы должны быть в безопасности с NOLOCK. Еще одна мысль: добавление значения в качестве включенного столбца в индексе Ix должно исключить поиск по Cx.

create nonclustered index Ix on Data (Id, Date) include (Value)
Другие вопросы по тегам