Какую конструкцию базы данных использовать для автоматической синхронизации данных при денормализации
Что проверяют
Знаете ли вы механизм, который держит копию данных согласованной без участия приложения, и понимаете ли его цену. Ожидаемый ответ: триггер, а для агрегатов индексированное или материализованное представление. Следом спрашивают, почему не делать это в коде на EF Core.
Как отвечать
Денормализация хранит одно значение в нескольких местах ради быстрого чтения, например сумму заказа в его шапке. Варианты синхронизации на стороне базы:
- триггер
AFTER INSERT, UPDATE, DELETE: срабатывает в той же транзакции, что и изменение, поэтому копия не расходится с оригиналом. В SQL Server триггер вызывается один раз на оператор, а изменённые строки лежат в псевдотаблицахinsertedиdeleted; - индексированное представление в SQL Server: представление
WITH SCHEMABINDINGс уникальным кластерным индексом, база обновляет его сама при каждом изменении таблиц. Подходит для сумм и счётчиков, но с ограничениями: без внешних соединений, сCOUNT_BIG(*)при группировке; - материализованное представление в PostgreSQL: обновляется только явным
REFRESH MATERIALIZED VIEW, автоматики нет; - вычисляемый столбец (
PERSISTEDв SQL Server,GENERATED ... STOREDв PostgreSQL): когда значение считается из полей той же строки.
Хранимая процедура сама ничего не синхронизирует, её кто-то должен вызвать. Репликация копирует данные между серверами, это другая задача.
Синхронизация в приложении (перехватчик SaveChanges, доменные события) проще тестируется, но её обходят ExecuteUpdate, сырой SQL и соседние сервисы. Между сервисами применяют outbox и брокер сообщений, это уже согласованность в конечном счёте, а не транзакционная.
Где спотыкаются
Пишут триггер в расчёте на одну строку: SELECT @id = id FROM inserted. При массовом UPDATE обработается одна произвольная строка, остальные копии разойдутся.
Забывают про EF Core: начиная с седьмой версии он сохраняет изменения через OUTPUT, что конфликтует с триггерами в SQL Server. Таблицу нужно пометить через HasTrigger в конфигурации модели.
Не называют цену: триггер удлиняет каждую запись и держит блокировки на связанных таблицах, а цепочки триггеров прячут логику от кода.