Блокировка CREATE TABLE

19

В другом приложении меня поразил плохой дизайн: несколько потоков одновременно выполняют EnsureDatabaseSchemaExists()метод, который выглядит в основном так:

IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'MyTable') AND type = N'U') BEGIN

    CREATE TABLE MyTable ( ... );

END

Однако, даже если он выполняется в транзакции SERIALIZABLE, этот код не кажется поточно-ориентированным (т. Е. Параллельный код пытается создать таблицу несколько раз). Есть ли шанс заставить оператор SELECT получить блокировку, которая мешает другому потоку выполнить тот же оператор SELECT?

Есть ли лучший шаблон для многопоточных методов EnsureSchemaExists ()?

DR
источник

Ответы:

18

Лучше всего использовать явную содержащую транзакцию и приобрести специальную эксклюзивную блокировку для защиты всей операции ( SELECTи CREATE TABLE) с помощью sp_getapplock . Системные объекты не учитывают запросы уровня изоляции и используют блокировки так же, как и пользовательские таблицы, по своему замыслу.

Условие состязания в исходном коде состоит в том, что несколько потоков могут сделать вывод, что таблица не существует до того, как какой-либо поток достигнет CREATE TABLEоператора.

Пол Уайт говорит, что GoFundMonica
источник
6
+1 просто убедитесь, что приложение блокирует проверку SELECT . В противном случае вы будете вводить тупики. В идеале можно было бы получить блокировку приложения в режиме S, проверить, обновить до X, но это сложно (по меньшей мере ...). Самый безопасный вариант - приобрести X, а затем выполнить развертывание всей схемы БД. Это должна быть редкая операция (например, при запуске приложения), поэтому блокировка X не должна иметь такого большого значения.
Ремус Русану
12

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

Реальный вопрос: почему DDL работает по требованию из нескольких xacts? Обычно обновление и миграция - это серьезный вопрос, который выполняется в выделенных временных окнах ... Вы не хотите, чтобы ваша миграция (сначала с кодом?) Неожиданно запускалась, некоторые из этих шагов обновления могут занять часы на большой таблице (размер операции с данными ...)

Ремус Русану
источник
3
Код - это своего рода DatabaseLogger, который создает свои таблицы по требованию. Нет миграции, нет забавных дел. Тем не менее, вы совершенно правы. Я собираюсь рефакторинг кода соответствующим образом.
DR
4
Также учтите, что развертывание / настройка совершенно нормально для запуска в контексте повышенных привилегий (например, администратором), но обычные операции - нет. В настоящее время вам требуется CREATE TABLEгрант для нормальных операций ...
Remus Rusanu