Beim Entwerfen großer relationaler Datenbanken treffen wir oft die Entscheidung, von einer normalen Form abzuweichen, d. h. Denormalisierung.
Die Gründe dafür können unterschiedlich sein, wie z. B. der Versuch, den Zugriff auf die angegebenen Daten zu beschleunigen, Beschränkungen der verwendeten Plattform/Frameworks/Entwicklungswerkzeuge und mangelnde Fähigkeiten eines Datenbankentwicklers/Designers.
Streng genommen ist ein Hinweis auf die Rahmenvorgaben etc. eigentlich ein Rechtfertigungsversuch für fehlende Kompetenzen.
Die denormalisierten Daten sind eine Schwachstelle, durch die es einfach ist, unsere Datenbank in einen nicht konsistenten (nicht integralen) Zustand zu bringen.
Was können wir damit machen?
Beispiel
In einer Datenbank gibt es eine Tabelle mit einigen Finanzoperationen:den Erhalt und die Verfügung von Geldern auf verschiedenen Konten.
Wir müssen immer den Kontostand kennen.
In den normalisierten Daten ist der Fondssaldo immer ein berechneter Wert. Wir berechnen die Summe der Einnahmen ohne Abbuchung.
Es ist jedoch zu aufwendig, bei vielen Operationen jedes Mal den Saldo zu berechnen. Daher wurde entschieden, den tatsächlichen Saldo in einer separaten Tabelle zu speichern. Wie aktualisieren wir die Daten in dieser Tabelle?
Die Lösung ist „wie gewohnt“
Fast in allen Informationssystemen, mit denen ich zu tun hatte, wurde diese Aufgabe von einer externen Anwendung übernommen, die die Geschäftslogik implementierte. Sie haben Glück, wenn die Anwendung einfach ist und es nur einen Datenänderungspunkt gibt, vom Formular in der Benutzeroberfläche. Was ist jedoch, wenn einige Importe, APIs, Anwendungen von Drittanbietern usw. von verschiedenen Personen und Teams durchgeführt werden? Was ist, wenn es mehrere Tabellen mit Summen statt einer gibt? Was ist, wenn es mehr als eine Tabelle mit Operationen gibt?
Es wird immer schwieriger zu überwachen, ob ein Entwickler beim Aktualisieren von Vorgängen eine Reihe von Tabellen aktualisiert hat. Die Daten verlieren ihre Integrität. Der Kontostand entspricht nicht den Operationen. Tests müssen solche Situationen natürlich aufdecken. Unsere Welt ist jedoch nicht ideal.
Auslöser
Alternativ werden Trigger verwendet, um die Integrität denormalisierter Daten zu kontrollieren.
Ich habe gehört, dass Trigger eine Datenbank stark verlangsamen, daher macht ihre Verwendung keinen Sinn.
Das zweite Argument war, dass die gesamte Logik in einer separaten Anwendung liegt und es nicht sinnvoll ist, die Geschäftslogik an verschiedenen Orten aufzubewahren.
Finden wir es heraus.
Lags
Innerhalb der Transaktion wird ein Trigger ausgelöst, der die Daten in der Tabelle ändert. Die Transaktion kann erst abgeschlossen werden, wenn der Trigger die erforderlichen Schritte ausgeführt hat. Daher lautet die Schlussfolgerung, dass Trigger „leicht“ sein müssen.
Das Beispiel für die „schwere“ Abfrage im Trigger lautet wie folgt:
update totals set total = select sum(operations.amount) from operations where operations.account = current_account where totals.account = current_account
Eine Abfrage bezieht sich auf die Tabelle mit Operationen und summiert den gesamten Betrag der Vorgänge für das Konto .
Wenn die Datenbank wächst, wird eine solche Abfrage immer mehr Zeit und Ressourcen verbrauchen. Wir können jedoch dasselbe Ergebnis erhalten, wenn wir die leichte Abfrage des folgenden Typs verwenden:
update totals set total = totals.total + current_amount where totals.account = current_account
Beim Hinzufügen einer neuen Zeile erhöht dieser Trigger einfach die Summe um das Konto, ohne sie zu berechnen. Die Summe hängt nicht von der Datenmenge in Tabellen ab. Es macht keinen Sinn, die Summe erneut zu berechnen, da wir sicher sein können, dass der Trigger jedes Mal ausgelöst wird, wenn eine neue Operation hinzugefügt wird.
Das Entfernen oder Ändern von Zeilen wird auf die gleiche Weise verarbeitet. Die Trigger dieses Typs verlangsamen den Betrieb nicht, gewährleisten jedoch die Datenkopplung und -integrität.
Jedes Mal, wenn ich beim Hinzufügen von Daten zu einer Tabelle mit einem Trigger „Verzögerungen“ erlebte, war dies ein Beispiel für eine so „schwere“ Abfrage. In den meisten Fällen war es möglich, es in eine „einfache“ Abfrage umzuschreiben.
Geschäftslogik
Wir müssen Funktionen, die Datenintegrität gewährleisten, von der Geschäftslogik unterscheiden. In jedem Fall stelle ich eine Frage, wenn die Daten normalisiert wären, würden wir eine solche Funktion brauchen? Wenn positiv, handelt es sich bei der Funktion um Geschäftslogik. Falls negativ, besteht die Funktion darin, Datenintegrität bereitzustellen. Sie können diese Funktionen in Trigger verpacken.
Es gibt jedoch die Meinung, dass es einfach ist, die gesamte Geschäftslogik über DBMS wie PostgreSQL oder Oracle zu implementieren.
Ich hoffe, dass dieser Artikel dazu beitragen wird, die Anzahl der Fehler in Ihrem Informationssystem zu reduzieren.
Natürlich bin ich weit davon entfernt zu glauben, dass alles, was hier geschrieben wird, die ultimative Wahrheit ist. Im wirklichen Leben ist natürlich alles viel komplizierter. Daher müssen Sie im Einzelfall eine Entscheidung treffen. Nutzen Sie Ihr technisches Denken!
P.S.
- In dem Artikel habe ich die Aufmerksamkeit auf den einzigen Aspekt der Verwendung von Triggern als mächtiges Werkzeug gelenkt.
- Der im Artikel beschriebene Ansatz ermöglicht es, Indizes in den Operationen zu vermeiden Tabelle, was wiederum das Hinzufügen von Daten zu dieser Tabelle beschleunigen kann. Bei hohen Lautstärken kompensiert dieser Ansatz leicht die Zeit, die am Abzug verbracht wird.
- Es ist wichtig zu verstehen, welche Tools wir verwenden müssen. In diesem Fall vermeiden Sie viele Probleme.