Как реализовать Сериализуемый уровень изоляции в SQL Server - PullRequest
0 голосов
/ 21 ноября 2018

Мне нужно реализовать сериализуемый уровень изоляции в SQL Server, но я пробовал много способов, и я не получил его.

Мне нужно заблокировать 1 строку в одной транзакции (это не имеет значенияесли заблокировать полную таблицу).Таким образом, другая транзакция не может даже выбрать строку (не читать).

Последнее, что я попробовал:

Для транзакции 1:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN

SELECT code FROM table1 WHERE code = 1

-- Here I select in another instance the same row

COMMIT TRAN

Длятранзакция 2:

BEGIN TRAN
SELECT code FROM table1 WHERE code = 1
COMMIT TRAN

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

Кто-нибудь может объяснить мне, если я что-то пропустил?

Ответы [ 3 ]

0 голосов
/ 21 ноября 2018

В соответствии с документацией :

SERIALIZABLE Указывает следующее:

  • Операторы не могут читать данные, которые были изменены, но еще не зафиксированыдругие транзакции.
  • Никакие другие транзакции не могут изменять данные, прочитанные текущей транзакцией, до тех пор, пока текущая транзакция не завершится.
  • Другие транзакции не могут вставлять новые строки со значениями ключа, которые попадают в диапазонключей, считанных любым оператором в текущей транзакции, пока текущая транзакция не завершится.

Если вы не вносите никаких изменений в данные с помощью INSERT, UPDATE или DELETE внутри транзакции 1, SQL освободит общую блокировку после завершения чтения.

Возможно, вы захотите попробовать добавить таблицу, чтобы предотвратить снятие блокировки строки до конца транзакции 1.

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRAN

SELECT code 
FROM table1 WITH(ROWLOCK, HOLDLOCK)
WHERE code = 1

COMMIT TRAN
0 голосов
/ 21 ноября 2018

Может быть, вы можете решить эту проблему с помощью некоторого хака, подобного этому?

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
BEGIN TRANSACTION
UPDATE someTableForThisHack set val = CASE WHEN val = 1 THEN 0 else 1 End
SELECT code from table1.....
COMMIT TRANSACTION

Таким образом, вы создаете таблицу someTableForThisHack и вставляете в нее одну строку.

0 голосов
/ 21 ноября 2018

SQL Server соответствует строгому определению Сериализуемого запроса.То есть должен быть результат, который может быть логически сгенерирован IF , оба запроса выполняются в последовательном порядке - транзакция 1 завершается до начала транзакции 2, или наоборот.

Это приводит к некоторымэффекты, которые могут отличаться от ожидаемых.На уровне SQLPerformance.com есть отличное объяснение уровня изоляции Serializable, в котором разъясняется, что в действительности означает эта логическая сериализуемость.(Очень полезный сайт, вот этот.)

Для ваших вышеупомянутых запросов нет логического требования запрещать второму запросу читать ту же строку, что и первый запрос.Независимо от того, в каком порядке выполняются запросы, они оба будут возвращать одни и те же данные без их изменения.Поскольку Query Analyzer может идентифицировать это, нет никаких причин устанавливать блокировку чтения данных.Однако, если один из запросов произвел обновление данных, то (предупреждение - логическое предположение здесь, поскольку я на самом деле не знаю, как SQL Server это обрабатывает), QA установит более сильную блокировку для выбранных строк.

TL; DR - SQL Server хочет минимизировать блокировку, поэтому он использует логический анализ, чтобы увидеть, какие типы блокировок необходимы для сериализуемого уровня изоляции, и он (пытается) использовать минимальное количество и силу блокировокнужно для достижения своей цели.

Теперь, когда мы с этим справились - есть только два способа, которыми я могу придумать, чтобы заблокировать строку, чтобы никто не мог ее прочитать: использование XLOCK + TABLOCK (блокировкався таблица - не рекомендуемая практика) или наличие какой-либо формы поля в каждой строке, которое обновляется при запуске процесса - что-то вроде поля SPID или битового флага для Locked.Когда вы обновляете его в своей транзакции, только SELECT с подсказками NOLOCK смогут прочитать его.

Ясно, что ни один из них не является оптимальным.Я рекомендую флаг «Эта строка занята - уходи», так как это, вероятно, подход, который я бы использовал для (почти) абсолютной блокировки строки.

...