2013-12-09 6 views
2

Скажем, у меня есть простой запрос, такой какИспользуя СО (NOLOCK) и сделок

Select * From MyTable WITH (NOLOCK) 

и, в то же время этот запрос выполняется, другой пользователь вставляет 100 строк в эту таблицу в контексте сделка.

Теоретически возможно, что оператор Select, поскольку он использует NOLOCK, может читать подмножество из 100 строк, вставленных в таблицу, до того, как транзакция будет выполнена или откат? Если я правильно понимаю NOLOCK, похоже, это может быть возможностью, но хотелось бы проверить.

SQL Server 2012

+0

На самом деле, я думаю, вы могли бы прочитать все 100 не только подмножество до фиксации или откат. – Paparazzi

+0

@Blam Не согласен. См. Мой обновленный ответ, который доказывает иначе. –

+0

@AaronBertrand - Я думаю, вы можете быть в разных целях. Я интерпретировал замечание Блама, говоря, что *, а также * чтение (правильного) подмножества затронутых строк, вы также можете прочитать весь набор строк до того, как они будут завершены/откатны. –

ответ

10

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

Доказательство # 1

Это очень легко доказать. В одном окне, создать эту таблицу:

CREATE TABLE dbo.what(id INT); 

Во втором окне, выполните этот запрос:

DECLARE @id INT; 

WHILE 1 = 1 
BEGIN 
SELECT @id = id FROM dbo.what WITH (NOLOCK) WHERE id = 2; 

IF @id = 2 
BEGIN 
    PRINT @id; 
    BREAK; 
END 
END 

Теперь вернитесь к первому окну, и начать намеренно длительную транзакцию, но рулет его обратно:

BEGIN TRANSACTION; 
GO 

INSERT dbo.what SELECT 2; 
GO 10000 

ROLLBACK TRANSACTION; 

Как только вы начинаете это в первом окне запроса во втором окне остановится и будет выплюнуть неизрасходованного значение, которое было зачитано.

Доказательство # 2

Это в первую очередь оспорить @ комментарий Бламу в выше, что я не согласен с:

На самом деле я думаю, что вы могли прочитать все 100 не только подмножество до к фиксации или откату.

Вы, безусловно, можете читать подмножества строк, затронутых транзакцией. Попробуйте следующий, похожий пример, на этот раз вставляя наборы 100 в таблицу 1000 раз и получая счет в запросе с помощью (NOLOCK). Окно # 1 (если вы еще не испытаны доказательство # 1 выше):

CREATE TABLE dbo.what(id INT); 

Window # 2:

DECLARE @c INT; 

WHILE 1 = 1 
BEGIN 
SELECT @c = COUNT(*) FROM dbo.what WITH (NOLOCK) WHERE id = 2; 

IF @c > 0 
    PRINT @c; 

IF @c > 10000 
    BREAK; 
END 

Назад в Window # 1:

BEGIN TRANSACTION; 
GO 

INSERT dbo.what SELECT TOP (100) 2 FROM sys.all_objects; 
GO 1000 

ROLLBACK TRANSACTION; 

Window # 2 будет вращаться, пока вы не начнете транзакцию. Как только вы это сделаете, вы начнете видеть подсчеты. Но они не будут насчитывать 100 (не считая 100 000, все или ничего не требуют @Blam, похоже, делают).Вот мои сокращенные результаты:

1 
10 
12 
14 
17 
19 
23 
25 
29 
... 
85 
87 
91 
95 
98 
100 
100 
... 
9700 
9700 
9763 
9800 
9838 
9900 
9936 
10000 
10000 
10000 
10080 

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

Другие побочные эффекты

Есть также случаи, когда NOLOCK может skip rows, or read the same row twice, в зависимости от типа сканирования, а когда другая транзакция генерации расщепляется страниц. По существу, происходит то, что запрос (NOLOCK) считывает данные, другие записи могут фактически перемещать данные в другое место - поскольку они могут - либо перемещать строку, которую вы уже прочитали, до точки вперёд в вашем сканировании, либо перемещать строку, которую вы еще не прочитали точку ранее в вашем сканировании.

Advice

В общем, это плохая новость, и вы должны рассмотреть READ_COMMITTED_SNAPSHOT вместо этого - он имеет такую ​​же выгоду позволяя читателям не блокировать писателей и наоборот, но дает вам согласованное представление данные в определенный момент времени, игнорируя все последующие изменения данных (это, однако, влияет на tempdb, поэтому обязательно проверьте его). Very thorough information here.

3

Как изящно объяснил Аарон, да, вы будете читать грязные незафиксированные данные. Но если вы хотите избежать чтения грязных данных и еще не готовы начать использовать оптимистичную блокировку, вы можете попробовать использовать подсказку таблицы READPAST, как показано ниже, хотя это приводит к тому, что она пропускает любые заблокированные строки, поэтому вы не увидите вставленных и обновленных строк, которые еще не были зафиксированы.

SELECT * 
FROM MyTable WITH (READPAST) 

Обратите внимание, что эта таблица подсказка требует, чтобы база данных работает в любом READ COMMITTED (по умолчанию) или повторяемые уровней изоляции READ.

Смежные вопросы