Прочитайте уровень обязательной изоляции в SQL-сервере для одного оператора - PullRequest
6 голосов
/ 23 января 2011

Скажем, у меня есть таблица персон, и в ней всего 1 строка -

id = 1, name = 'foo'

На одном соединении

select p1.id, p1.name, p2.name
from person p1 
join person p2 on p1.id = p2.id

На другом соединении одновременно:

update person set name = 'bar' where person.id = 1

Q1: Возможно ли, по моему выбору, вернуть результат, подобный этому, на основе времени оператора обновления:

id = 1, p1.name = 'foo', p2.name = 'bar'

Ни одно соединение не использует явноетранзакция и оба используют уровень изоляции транзакции по умолчанию READ COMMITTED.

Вопрос заключается в том, чтобы помочь мне понять, продолжают ли существовать блокировки, полученные в начале оператора SQL, до тех пор, пока оператор не завершится, или если онвозможно ли для оператора снять блокировку и повторно получить блокировку для той же строки, если она используется дважды в одном и том же операторе?

Q2: Изменится ли ответ на вопрос, если на БД установлен set read_committed_snapshot on?

1 Ответ

9 голосов
/ 23 января 2011

Q1: Да, это вполне возможно, по крайней мере, в теории. read committed просто гарантирует, что вы не читаете грязные данные, не дает никаких обещаний о согласованности. На уровне фиксации чтения общие блокировки снимаются, как только считываются данные (не в конце транзакции или даже в конце оператора)

Q2: Да, ответ на этот вопрос изменится, если включен read_committed_snapshot. Тогда вам будет гарантирована согласованность уровня оператора . Мне трудно найти источник в Интернете, который однозначно заявляет об этом, но цитируя стр.648 «Microsoft SQL Server 2008 Internals»

Заявление в RCSI видит все совершено до начала заявление. Каждое новое утверждение в транзакция забирает самые последние совершенные изменения.

Также см. Это сообщение в блоге MSDN

Сценарий установки

CREATE TABLE person 
(
id int primary key,
name varchar(50)
)

INSERT INTO person
values(1, 'foo');

Соединение 1

while 1=1
update person SET name = CASE WHEN name='foo' then 'bar' ELSE 'foo' END

Соединение 2

DECLARE @Results TABLE (
  id    int primary key,
  name1 varchar(50),
  name2 varchar(50))

while( NOT EXISTS(SELECT *
                  FROM   @Results) )
  BEGIN
      INSERT INTO @Results
      Select p1.id,
             p1.name,
             p2.name
      from   person p1
             INNER HASH join person p2
               on p1.id = p2.id
      WHERE  p1.name <> p2.name
  END

SELECT *
FROM   @Results  

Результаты

id          name1 name2
----------- ----- -----
1           bar   foo

Глядя на другие типы соединений в Profiler, выясняется, что эта проблема не могла возникнуть ни по плану merge join, ни по nested loops для этого конкретного запроса (блокировки не снимаются до тех пор, пока не будут получены все), но остается одна причина read committed просто гарантирует, что вы не читаете грязные данные, он не дает никаких обещаний о согласованности. На практике вы можете не получить эту проблему для точного отправленного вами запроса, поскольку SQL Server не будет выбирать этот тип соединения по умолчанию. Однако вам остается только полагаться на детали реализации, чтобы получить желаемое поведение.

Trace

Примечание: если вам интересно, почему некоторые блокировки на уровне строки S, по-видимому, отсутствуют, то это , здесь описана оптимизация .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...