Есть ли возможное условие гонки в этом утверждении 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
может также сделать работу...