В SQL Server, как работают блокировки чтения?

17

Предположим, у меня есть следующий длительный запрос

UPDATE [Table1]
SET [Col1] = 'some value'
WHERE [Col2] -- some clause which selects thousands of rows

и предположим, что следующий запрос выполняется во время выполнения вышеуказанного запроса

SELECT *
FROM [Table1]

Предотвращает ли первый запрос выполнение второго запроса до тех пор, пока не будет выполнен первый запрос? Если да, предотвращает ли первый запрос выполнение второго запроса на всех строках или только на строках, включенных в предложение WHERE?

РЕДАКТИРОВАТЬ:

Предположим, что второй запрос

SELECT [Col1], [Col2]
FROM [Table1]
WHERE [Col2] -- some clause whose matching elements overlap those from
             -- the clause in the first query and which has additional matching elements
cm007
источник

Ответы:

14

Я рекомендую прочитать « Понимание того, как SQL Server выполняет запрос , в нем объясняется, как работают операции чтения и записи и как работает блокировка».

Вид 10000 футов выглядит следующим образом:

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

Это действительно только верхушка айсберга. Тема обширна. В вашем примере никто не может ответить на ваш вопрос о том, что на самом деле блокируется, потому что это будет зависеть от многих факторов. Конечно, ни одно приложение не должно выдавать, SELECT * FROM Table1 потому что в нем отсутствует предложение WHERE и оно используется *. Это плохие практики, потому что, помимо прочего, они точно приведут к блокированию раздоров.

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

Ремус Русану
источник
Что если мне понадобится все содержимое таблицы (скажем, в ней всего 14 строк)? Как это плохо, SELECT * FROM Table1если это именно то, что мне нужно?
Азимут
1
*само по себе это плохая практика, потому что при изменении структуры таблицы приложение обычно ломается (в результате появляются неожиданные столбцы).
Ремус Русану
3

Редактировать: Как указывает @MaxVernon , следующее ни в коем случае не является предложением использовать NOLOCK , и я очень хорошо должен был упомянуть, что установил уровень транзакции READ UNCOMMITEDи позволил негативной коннотации стоять там, чем NOLOCKподниматься в первую очередь. Итак, как первоначально размещено:

Быстрый и простой ответ: «Да, первый запрос заблокирует второй запрос, если не указана определенная подсказка индекса ( NOLOCK , иногда называемая« грязным чтением »), или если уровень изоляции транзакции второго запроса установлен на READ UNCOMMITED(который работает идентично), нет."

В ответ на дополнительные детали, представленные в вопросе, которые влекут за собой включение WITHвторого предложения SELECT, являющегося взаимоисключающим или иным образом, взаимодействия между этими двумя запросами будут в основном одинаковыми.

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'Foo'
                    AND type = 'U' )
BEGIN
    --DROP TABLE dbo.Foo;
    CREATE TABLE dbo.Foo
    (
        Foo_PK          BIGINT IDENTITY( 1, 1 ) NOT NULL,
                            PRIMARY KEY ( Foo_PK ),
        Bar             BIT,
        x               BIT,
        y               BIT,
        z               BIT
    );

    CREATE NONCLUSTERED INDEX IX_Foo_x
        ON  dbo.Foo ( x );

    INSERT INTO dbo.Foo ( Bar, x, y, z )
    VALUES ( 1, 1, 1, 1 ), ( 0, 0, 0, 0 );
END;    
GO

BEGIN TRANSACTION;

UPDATE  dbo.Foo
    SET y = 0
WHERE   x = 1;

-- COMMIT TRANSACTION;

В отдельном сеансе выполните следующее:

SELECT  *
FROM    dbo.Foo WITH ( NOLOCK );
GO

SELECT  *
FROM    dbo.Foo;

Вы можете проверить блокировки, которые в настоящее время удерживаются, запустив sp_lock, желательно в еще одном отдельном сеансе:

EXECUTE dbo.sp_lock;

Вы должны увидеть, что KEYблокировка типа удерживается спидом, выполняющим транзакцию вставки в X(исключительном) режиме, не путаясь с другими IX( намеренно исключающими ) блокировками. Документация о блокировке указывает, что хотя KEYблокировка зависит от диапазона, она также не позволяет другим транзакциям вставлять или обновлять затронутые столбцы, изменяя содержащиеся в них данные, чтобы они могли попадать в этот диапазон исходного запроса. Поскольку удерживаемая блокировка является исключительной, первый запрос запрещает доступ к ресурсу из любой другой параллельной транзакции. Фактически все строки столбца блокируются независимо от того, попадают ли они в диапазон, указанный в первом запросе.

Таким образом, Sблокировка, удерживаемая вторым сеансом, будет WAITдействовать до тех пор, пока Xблокировка не будет снята , не позволяя другой X(или U) блокировке принять этот ресурс с другого параллельного spid до того, как второй сеанс завершит свою операцию чтения, оправдывая существование Sблокировки.

Теперь отредактируйте для ясности: если я не ошибаюсь в том, что грязное чтение взято из краткого описания рисков, упомянутых здесь ... Редактировать 3 : Я только что понял, что не рассматриваю эффект фоновой контрольной точки, которая пишет как о еще не совершенной транзакции на диск, так что да, мое объяснение вводило в заблуждение.

Во втором запросе первая партия может (и в этом случае будет) возвращать незафиксированные данные. Второй пакет, работающий с уровнем изоляции транзакции по умолчанию READ COMMITED, вернется только после завершения фиксации или отката в первом сеансе.

Отсюда вы можете посмотреть свои планы запросов и соответствующие уровни блокировки, но, что еще лучше, вы можете прочитать все о блокировках в SQL Server здесь .

Avarkx
источник
1
В WITH (NOLOCK)этом случае было бы полезно предупредить об использовании . См. Brentozar.com/archive/2011/11/… и brentozar.com/archive/2013/02/… для получения дополнительной информации.
Макс Вернон
3
О, WITH (NOLOCK)подсказка не возвращает грязные страницы из памяти, которые не были зафиксированы. На самом деле он читает строки из таблицы (будь то на диске или кэшированные в памяти), не блокируя писателей от обновления или добавления строк на страницы, используемые таблицей.
Макс Вернон
2
Я не совсем понимаю. Если ответ «1-й запрос мешает запуску 2-го?» «Нет», как ответ на второй вопрос «Да»? Можете ли вы уточнить, на какие вопросы вы отвечаете, и расширить свои ответы?
Джон на все руки
Редактирует в изобилии, извините, ребята! Дайте мне знать, если есть что-то еще, что неясно!
Аварк