Когато проектираме големи релационни бази данни, ние често вземаме решение да се отклоним от нормална форма, т.е. денормализация.
Причините за това могат да бъдат различни, като опит за ускоряване на достъпа до посочените данни, ограничения на използваната платформа/рамка/инструменти за разработка и липса на умения на разработчик/дизайнер на база данни.
Строго погледнато, позоваването на рамковите ограничения и т.н. всъщност е опит да се оправдае липсата на умения.
Денормализираните данни са уязвимост, чрез която е лесно да доведем нашата база данни до непоследователно (неинтегрално) състояние.
Какво можем да направим с това?
Пример
В база данни има таблица с някои финансови операции:получаване и разпореждане със средства по различни сметки.
Винаги трябва да знаем баланса на сметката.
В нормализираните данни салдото на фонда винаги е изчислена стойност. Ще изчислим общата сума на разписките без дебитиране.
Въпреки това е твърде скъпо да се изчислява балансът всеки път, когато има много операции. Поради това беше решено действителният баланс да се съхранява в отделна таблица. Как да актуализираме данните в тази таблица?
Решението е „както обикновено“
Почти във всички информационни системи, с които трябваше да работя, тази задача се изпълняваше от външно приложение, което реализира бизнес логиката. Имате късмет, ако приложението е просто и има само една точка за промяна на данните, от формата в потребителския интерфейс. Но какво ще стане, ако има някои импортирания, API, приложения на трети страни и т.н., извършени от различни хора и екипи? Ами ако има няколко таблици със суми вместо една? Ами ако има повече от една таблица с операции?
Става по-трудно да се следи дали разработчикът е актуализирал куп таблици при актуализиране на операции. Данните губят целостта. Салдото по сметката не съответства на операции. Разбира се, тестването трябва да разкрие такива ситуации. Нашият свят обаче не е идеален.
Задействания
Като алтернатива се използват тригери за контрол на целостта на денормализираните данни.
Чух, че тригерите забавят значително база данни, така че използването им няма смисъл.
Вторият аргумент беше, че цялата логика се крие в отделно приложение и поддържането на бизнес логиката на различни места е неразумно.
Нека разберем.
Закъснения
В транзакцията се задейства тригер, който променя данните в таблицата. Транзакцията не може да бъде завършена, докато тригерът не изпълни необходимите стъпки. Следователно заключението е, че тригерите трябва да са „леки“.
Примерът за „тежката“ заявка в тригера е следният:
update totals set total = select sum(operations.amount) from operations where operations.account = current_account where totals.account = current_account
Заявката се отнася до таблицата соперации и обобщава общата сума на операции за сметката .
Когато базата данни се увеличи, такава заявка ще отнема все повече време и ресурси. Въпреки това можем да получим същия резултат, като използваме леката заявка от следния тип:
update totals set total = totals.total + current_amount where totals.account = current_account
Когато добавяте нов ред, този тригер просто ще увеличи общата сума за сметката, без да я изчислява. Общата сума не зависи от количеството данни в таблици. Няма смисъл да изчисляваме общата сума отново, тъй като можем да сме сигурни, че тригерът се задейства всеки път, когато добавяте нова операция.
Премахването или промяната на редове се обработва по същия начин. Тригерите от този тип няма да забавят операциите, но ще осигурят свързване и целостта на данните.
Всеки път, когато изпитвах „закъснения“ при добавяне на данни към таблица със задействане, това беше пример за такава „тежка“ заявка. В повечето случаи е било възможно да се пренапише в „лесна“ заявка.
Бизнес логика
Трябва да разграничим функциите, които осигуряват целостта на данните от бизнес логиката. Във всеки случай задавам въпрос, ако данните бяха нормализирани, ще ни трябва ли такава функция? Ако е положителен, функцията е бизнес логика. Ако е отрицателен, функцията е да осигури целостта на данните. Можете да обвиете тези функции в тригери.
Има обаче мнение, че е лесно да се внедри цялата бизнес логика чрез СУБД, като PostgreSQL или Oracle.
Надявам се тази статия да помогне за намаляване на броя на грешките във вашата информационна система.
Разбира се, далеч съм от мисълта, че всичко написано тук е истината в последно време. В реалния живот, разбира се, всичко е много по-сложно. Следователно трябва да вземете решение във всеки конкретен случай. Използвайте инженерното си мислене!
P.S.
- В статията обърнах внимание на единствения аспект на използването на тригери като мощен инструмент.
- Подходът, описан в статията, позволява избягване на индекси в Операции таблица, което от своя страна може да ускори процеса на добавяне на данни към тази таблица. При големи обеми този подход лесно компенсира времето, прекарано на спусъка.
- Важно е да разберем за какви инструменти трябва да използваме. В този случай ще избегнете много проблеми.