Есть ли возможное условие гонки в этом утверждении UPDATE?

Я пишу программу-синхронизатор, которая будет принимать все изменения в одной БД и синхронизировать их с другой БД. Для этого я добавил в свою таблицу T две колонки:

alter table T add LastUpdate rowversion, LastSync binary(8) not null default 0

Теперь я могу легко выбрать все строки, которые изменились с момента последней синхронизации:

select * from T where LastUpdate > LastSync

Однако после выполнения синхронизации я должен сделать два поля равными. Но обновление строки также обновляет метку времени, поэтому я должен сделать это:

update T set LastSync=@@DBTS+1 where ID=@syncedId

Но мне интересно - это всегда будет работать? Что если я прочту значение @@DBTS и тогда другому пользователю удастся вставить / обновить строку где-то до того, как моя строка будет зафиксирована? Это рискованный код? И если да - как это можно сделать лучше?

2 ответа

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

Ваше программное обеспечение синхронизатора может работать следующим образом:

  • Получить значение @LastSync из дополнительной таблицы
  • "Выберите @ThisSync = max(LastUpdate) из T, где LastUpdate > @LastSync"
  • "Выберите * из T, где LastUpdate > @LastSync и LastUpdate <= @ThisSync" - ваши строки для синхронизации
  • Сохраните @ThisSync как новый "LastSync" в дополнительной таблице.

Записи, измененные во время выполнения синхронизации, будут иметь более высокое значение инверсии строки, чем запрос max(). Они будут синхронизированы при следующем вызове вашего синхронизатора.

Если вы запустите это в Serializable транзакция, то никакие другие операции чтения / записи не смогут повлиять на эти таблицы.

RepeateableRead может также сделать работу...

Другие вопросы по тегам