566 Stimmen

Fehler bei der Datenbankentwicklung, die von Anwendungsentwicklern gemacht werden

Was sind häufige Fehler bei der Datenbankentwicklung, die von Anwendungsentwicklern gemacht werden?

1001voto

cletus Punkte 596503

1. Keine Verwendung geeigneter Indizes

Das ist relativ einfach, aber es passiert trotzdem immer wieder. Fremdschlüssel sollten mit Indizes versehen werden. Wenn Sie ein Feld in einer WHERE sollten Sie (wahrscheinlich) einen Index haben. Solche Indizes sollten oft mehrere Spalten abdecken, je nach den Abfragen, die Sie ausführen müssen.

2. Keine Durchsetzung der referentiellen Integrität

Ihre Datenbank kann hier variieren, aber wenn Ihre Datenbank referentielle Integrität unterstützt - d. h., dass alle Fremdschlüssel garantiert auf eine existierende Entität verweisen - sollten Sie sie verwenden.

Dieser Fehler ist bei MySQL-Datenbanken recht häufig zu beobachten. Ich glaube nicht, dass MyISAM dies unterstützt. InnoDB schon. Sie werden Leute finden, die MyISAM verwenden, oder solche, die InnoDB verwenden, es aber trotzdem nicht einsetzen.

Mehr dazu hier:

3. Verwendung natürlicher Primärschlüssel anstelle von (technischen) Surrogatschlüsseln

Natürliche Schlüssel sind Schlüssel, die auf extern aussagekräftigen Daten basieren und (angeblich) eindeutig sind. Gängige Beispiele sind Produktcodes, zweistellige Bundesstaatencodes (USA), Sozialversicherungsnummern usw. Surrogat- oder technische Primärschlüssel sind solche, die außerhalb des Systems absolut keine Bedeutung haben. Sie dienen lediglich der Identifizierung der Entität und sind in der Regel automatisch inkrementierende Felder (SQL Server, MySQL, andere) oder Sequenzen (vor allem Oracle).

Meiner Meinung nach sollten Sie immer Surrogatschlüssel verwenden. Dieses Problem ist in diesen Fragen aufgetaucht:

Dies ist ein etwas kontroverses Thema, bei dem Sie keine allgemeine Zustimmung finden werden. Es gibt zwar einige Leute, die natürliche Schlüssel in manchen Situationen für in Ordnung halten, aber es gibt keine Kritik an Ersatzschlüsseln, außer dass sie wohl unnötig sind. Das ist ein ziemlich kleiner Nachteil, wenn Sie mich fragen.

Denken Sie daran, dass auch Länder können aufhören zu existieren (zum Beispiel Jugoslawien).

4. Schreiben von Abfragen, die Folgendes erfordern DISTINCT zur Arbeit

Sie sehen dies häufig in ORM-generierten Abfragen. Schauen Sie sich die Protokollausgabe von Hibernate an und Sie werden sehen, dass alle Abfragen mit beginnen:

SELECT DISTINCT ...

Dies ist eine Art Abkürzung, um sicherzustellen, dass Sie keine doppelten Zeilen zurückgeben und somit doppelte Objekte erhalten. Sie werden manchmal sehen, dass auch andere dies tun. Wenn Sie das zu oft sehen, ist das ein echtes Warnsignal. Nicht das DISTINCT schlecht ist oder keine gültigen Anwendungen hat. Das tut es (in beiderlei Hinsicht), aber es ist kein Ersatz oder eine Notlösung für das Schreiben korrekter Abfragen.

Von Warum ich DISTINCT hasse :

Wo es bei mir anfängt, böse zu werden ist, wenn ein Entwickler umfangreiche Abfragen erstellt, Tabellen Tabellen miteinander verbindet, und plötzlich merkt er, dass es siehe wie er ist doppelte (oder sogar mehr) Zeilen erhält und seine unmittelbare Antwort...sein "Lösung" für dieses "Problem" ist das Schlüsselwort DISTINCT zu verwenden und POOF alle seine Probleme verschwinden.

5. Bevorzugung der Aggregation gegenüber Joins

Ein weiterer häufiger Fehler von Entwicklern von Datenbankanwendungen besteht darin, dass sie nicht erkennen, wie viel teurer die Aggregation (also die GROUP BY Klausel) kann mit Joins verglichen werden.

Um Ihnen eine Vorstellung davon zu geben, wie weit verbreitet dies ist, habe ich hier mehrmals über dieses Thema geschrieben und wurde dafür oft heruntergestuft. Zum Beispiel:

Von SQL-Anweisung - "join" vs. "group by und having" :

Erste Anfrage:

SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3

Abfragezeit: 0,312 s

Zweite Anfrage:

SELECT t1.userid
FROM userrole t1
JOIN userrole t2 ON t1.userid = t2.userid AND t2.roleid = 2
JOIN userrole t3 ON t2.userid = t3.userid AND t3.roleid = 3
AND t1.roleid = 1

Abfragezeit: 0,016 s

Das ist richtig. Die Verbindungsversion, die ich vorgeschlagene Version ist zwanzigmal schneller als die aggregierte Version.

6. Keine Vereinfachung komplexer Abfragen durch Ansichten

Nicht alle Datenbankanbieter unterstützen Ansichten, aber bei denjenigen, die sie unterstützen, können sie Abfragen erheblich vereinfachen, wenn sie vernünftig eingesetzt werden. Bei einem Projekt habe ich zum Beispiel eine generisches Parteimodell für CRM. Dies ist eine äußerst leistungsfähige und flexible Modellierungstechnik, die jedoch zu zahlreichen Verknüpfungen führen kann. In diesem Modell gab es:

  • Party : Menschen und Organisationen;
  • Rolle der Partei : Dinge, die diese Parteien getan haben, zum Beispiel Arbeitnehmer und Arbeitgeber;
  • Partei Rolle Beziehung wie diese Rollen zueinander in Beziehung stehen.

Beispiel:

  • Ted ist eine Person und damit ein Subtyp der Partei;
  • Ted hat viele Rollen, eine davon ist die des Mitarbeiters;
  • Intel ist eine Organisation, die eine Unterart einer Partei ist;
  • Intel hat viele Rollen, eine davon ist die des Arbeitgebers;
  • Intel beschäftigt Ted, was bedeutet, dass es eine Beziehung zwischen ihren jeweiligen Rollen gibt.

Es gibt also fünf Tabellen, die miteinander verbunden sind, um Ted mit seinem Arbeitgeber zu verbinden. Sie gehen davon aus, dass es sich bei allen Mitarbeitern um Personen (und nicht um Organisationen) handelt, und stellen diese Hilfssicht bereit:

CREATE VIEW vw_employee AS
SELECT p.title, p.given_names, p.surname, p.date_of_birth, p2.party_name employer_name
FROM person p
JOIN party py ON py.id = p.id
JOIN party_role child ON p.id = child.party_id
JOIN party_role_relationship prr ON child.id = prr.child_id AND prr.type = 'EMPLOYMENT'
JOIN party_role parent ON parent.id = prr.parent_id = parent.id
JOIN party p2 ON parent.party_id = p2.id

Und plötzlich haben Sie eine sehr einfache Sicht auf die gewünschten Daten, aber auf einem sehr flexiblen Datenmodell.

7. Keine Bereinigung der Eingaben

Das ist eine große Sache. Ich mag PHP, aber wenn man nicht weiß, was man tut, ist es sehr einfach, Websites zu erstellen, die anfällig für Angriffe sind. Nichts fasst das besser zusammen als die Geschichte des kleinen Bobby Tables .

Daten, die der Nutzer in Form von URLs, Formulardaten und Kekse sollten immer als feindlich und unschädlich behandelt werden. Stellen Sie sicher, dass Sie bekommen, was Sie erwarten.

8. Keine Verwendung vorbereiteter Erklärungen

Bei vorbereiteten Anweisungen kompilieren Sie eine Abfrage abzüglich der Daten, die bei Einfügungen, Aktualisierungen und WHERE Klauseln und liefern diese dann später. Zum Beispiel:

SELECT * FROM users WHERE username = 'bob'

gegen

SELECT * FROM users WHERE username = ?

o

SELECT * FROM users WHERE username = :username

abhängig von Ihrer Plattform.

Ich habe gesehen, wie Datenbanken auf diese Weise in die Knie gezwungen wurden. Im Grunde muss jede moderne Datenbank jedes Mal, wenn sie auf eine neue Abfrage stößt, diese kompilieren. Wenn sie auf eine Abfrage stößt, die sie schon einmal gesehen hat, geben Sie der Datenbank die Möglichkeit, die kompilierte Abfrage und den Ausführungsplan zwischenzuspeichern. Indem Sie die Abfrage häufig ausführen, geben Sie der Datenbank die Möglichkeit, dies herauszufinden und entsprechend zu optimieren (z. B. durch Anheften der kompilierten Abfrage im Speicher).

Durch die Verwendung von vorbereiteten Anweisungen erhalten Sie außerdem aussagekräftige Statistiken darüber, wie häufig bestimmte Abfragen verwendet werden.

Vorbereitete Anweisungen schützen Sie auch besser vor SQL-Injection-Angriffen.

9. Nicht genug normalisiert

Normalisierung der Datenbank ist im Grunde der Prozess der Optimierung des Datenbankdesigns oder der Organisation Ihrer Daten in Tabellen.

Erst diese Woche bin ich auf einen Code gestoßen, bei dem jemand ein Array implodiert und in ein einzelnes Feld in einer Datenbank eingefügt hat. Eine Normalisierung würde bedeuten, dass ein Element dieses Arrays als separate Zeile in einer untergeordneten Tabelle behandelt wird (d.h. eine Eins-zu-Viel-Beziehung).

Dies war auch Thema in Beste Methode zum Speichern einer Liste von Benutzer-IDs :

Ich habe in anderen Systemen gesehen, dass die Liste in einem serialisierten PHP-Array gespeichert wird.

Der Mangel an Normalisierung hat jedoch viele Formen.

Mehr:

10. Zuviel Normalisierung

Dies mag wie ein Widerspruch zum vorherigen Punkt erscheinen, aber die Normalisierung ist, wie vieles, ein Werkzeug. Sie ist ein Mittel zum Zweck und nicht ein Ziel an sich. Ich glaube, viele Entwickler vergessen das und fangen an, ein "Mittel" als "Zweck" zu behandeln. Unit-Tests sind ein Paradebeispiel dafür.

Ich habe einmal an einem System gearbeitet, das eine riesige Hierarchie für Kunden hatte, die in etwa so aussah:

Licensee ->  Dealer Group -> Company -> Practice -> ...

so dass man etwa 11 Tabellen miteinander verbinden musste, bevor man aussagekräftige Daten erhielt. Dies war ein gutes Beispiel für eine zu weit gehende Normalisierung.

Genauer gesagt, kann eine sorgfältige und wohlüberlegte Denormalisierung enorme Leistungsvorteile bringen, aber man muss dabei sehr vorsichtig sein.

Mehr:

11. Verwendung exklusiver Bögen

Ein exklusiver Bogen ist ein häufiger Fehler, bei dem eine Tabelle mit zwei oder mehr Fremdschlüsseln erstellt wird, von denen nur einer nicht null sein kann. Ein großer Fehler. Zum einen wird es sehr viel schwieriger, die Datenintegrität zu wahren. Denn selbst bei referenzieller Integrität hindert nichts daran, dass zwei oder mehr dieser Fremdschlüssel gesetzt werden (ungeachtet komplexer Prüfbeschränkungen).

Von Ein praktischer Leitfaden für den Entwurf relationaler Datenbanken :

Wir haben von der ausschließlichen Bogenkonstruktion dringend abgeraten, wo immer aus dem guten Grund abgeraten, dass sie umständlich zu programmieren sein können und mehr Schwierigkeiten bei der Wartung mit sich bringen.

12. Überhaupt keine Leistungsanalyse von Abfragen durchführen

Vor allem in der Welt der Datenbanken herrscht Pragmatismus vor. Wenn Sie so sehr an Prinzipien festhalten, dass sie zu einem Dogma geworden sind, dann haben Sie höchstwahrscheinlich Fehler gemacht. Nehmen Sie das Beispiel der Aggregatabfragen von oben. Die Aggregatversion mag "nett" aussehen, aber ihre Leistung ist miserabel. Ein Leistungsvergleich hätte die Debatte beenden sollen (hat er aber nicht), aber noch wichtiger ist, dass es ignorant, ja sogar gefährlich ist, solche schlecht informierten Ansichten überhaupt erst zu verbreiten.

13. Übermäßiger Rückgriff auf UNION ALL und insbesondere UNION-Konstrukte

Eine UNION in SQL-Begriffen verkettet lediglich kongruente Datensätze, d.h. sie haben den gleichen Typ und die gleiche Anzahl von Spalten. Der Unterschied zwischen ihnen besteht darin, dass UNION ALL eine einfache Verkettung ist und wo immer möglich bevorzugt werden sollte, während UNION implizit eine DISTINCT durchführt, um doppelte Tupel zu entfernen.

UNIONs, wie auch DISTINCT, haben ihre Berechtigung. Es gibt sinnvolle Anwendungen. Wenn Sie jedoch feststellen, dass Sie sie häufig verwenden, insbesondere in Unterabfragen, dann machen Sie wahrscheinlich etwas falsch. Das könnte ein Fall von schlechter Abfragekonstruktion oder einem schlecht entworfenen Datenmodell sein, das Sie zu solchen Dingen zwingt.

UNIONs, insbesondere wenn sie in Joins oder abhängigen Unterabfragen verwendet werden, können eine Datenbank lahm legen. Versuchen Sie, sie wann immer möglich zu vermeiden.

14. Verwendung von OR-Bedingungen in Abfragen

Das mag harmlos erscheinen. Schließlich sind die UNDs in Ordnung. OR sollte also auch in Ordnung sein, oder? Falsch. Im Grunde genommen ist eine AND-Bedingung schränkt ein. den Datensatz, während eine ODER-Bedingung wächst aber nicht in einer Weise, die für eine Optimierung geeignet ist. Insbesondere dann, wenn sich die verschiedenen ODER-Bedingungen überschneiden könnten, so dass der Optimierer gezwungen ist, das Ergebnis effektiv mit einer DISTINCT-Operation zu bearbeiten.

Schlecht:

... WHERE a = 2 OR a = 5 OR a = 11

Besser:

... WHERE a IN (2, 5, 11)

Nun kann Ihr SQL-Optimierer die erste Abfrage tatsächlich in die zweite umwandeln. Vielleicht aber auch nicht. Tun Sie es einfach nicht.

15. Sie haben ihr Datenmodell nicht so gestaltet, dass es für leistungsstarke Lösungen geeignet ist.

Dieser Punkt ist schwer zu quantifizieren. Er wird in der Regel anhand seiner Auswirkungen beobachtet. Wenn Sie feststellen, dass Sie für relativ einfache Aufgaben komplizierte Abfragen schreiben oder dass Abfragen zur Ermittlung relativ einfacher Informationen nicht effizient sind, dann haben Sie wahrscheinlich ein schlechtes Datenmodell.

In gewisser Weise fasst dieser Punkt alle vorangegangenen zusammen, aber er ist eher ein warnendes Beispiel dafür, dass Dinge wie die Optimierung von Abfragen oft als erstes getan werden, obwohl sie als zweites getan werden sollten. Zuallererst sollten Sie sicherstellen, dass Sie ein gutes Datenmodell haben, bevor Sie versuchen, die Leistung zu optimieren. Wie Knuth sagte:

Vorzeitige Optimierung ist die Wurzel allen Übels

16. Falsche Verwendung von Datenbanktransaktionen

Alle Datenänderungen für einen bestimmten Prozess sollten atomar sein. Das heißt, wenn der Vorgang erfolgreich ist, ist er vollständig abgeschlossen. Schlägt sie fehl, werden die Daten unverändert gelassen. - Es sollte keine Möglichkeit für "halbfertige" Änderungen geben.

Der einfachste Weg, dies zu erreichen, besteht darin, dass das gesamte Systemdesign darauf abzielt, alle Datenänderungen durch einzelne INSERT/UPDATE/DELETE-Anweisungen zu unterstützen. In diesem Fall ist keine besondere Transaktionsverarbeitung erforderlich, da Ihre Datenbank-Engine dies automatisch tun sollte.

Wenn jedoch Prozesse erfordern, dass mehrere Anweisungen als Einheit ausgeführt werden, um die Daten in einem konsistenten Zustand zu halten, dann ist eine angemessene Transaktionskontrolle erforderlich.

  • Beginnen Sie eine Transaktion vor der ersten Anweisung.
  • Schließen Sie die Transaktion nach der letzten Anweisung ab.
  • Wenn ein Fehler auftritt, rollen Sie die Transaktion zurück. Und ganz wichtig! Vergessen Sie nicht, alle Anweisungen, die nach dem Fehler folgen, zu überspringen/abzubrechen.

Es wird auch empfohlen, sorgfältig darauf zu achten, wie Ihre Datenbank-Konnektivitätsschicht und Ihre Datenbank-Engine in dieser Hinsicht interagieren.

17. Unverständnis für das Paradigma des "set-based

Die SQL-Sprache folgt einem spezifischen Paradigma, das für bestimmte Arten von Problemen geeignet ist. Ungeachtet verschiedener herstellerspezifischer Erweiterungen hat die Sprache Schwierigkeiten, Probleme zu lösen, die in Sprachen wie Java, C#, Delphi usw. trivial sind.

Dieser Mangel an Verständnis äußert sich auf verschiedene Weise.

  • Unangemessenes Auferlegen von zu viel prozeduraler oder imperativer Logik auf die Datenbasis.
  • Unangemessene oder übermäßige Verwendung von Cursorn. Insbesondere, wenn eine einzige Abfrage ausreichen würde.
  • Fälschlicherweise wird angenommen, dass Trigger bei Aktualisierungen mit mehreren Zeilen einmal pro betroffener Zeile ausgelöst werden.

Legen Sie eine klare Aufteilung der Zuständigkeiten fest und bemühen Sie sich, für jedes Problem das geeignete Instrument einzusetzen.

110voto

Die wichtigsten Datenbankdesign- und Programmierfehler von Entwicklern

  • Egoistische Datenbankgestaltung und -nutzung. Entwickler behandeln die Datenbank oft als ihren persönlichen persistenten Objektspeicher, ohne die Bedürfnisse der anderen Beteiligten an den Daten zu berücksichtigen. Dies gilt auch für Anwendungsarchitekten. Schlechtes Datenbankdesign und Datenintegrität erschweren die Arbeit Dritter mit den Daten und können die Lebenszykluskosten des Systems erheblich erhöhen. Berichtswesen und MIS sind in der Regel ein stiefmütterlich behandelter Teil des Anwendungsdesigns und werden nur als nachträglicher Einfall durchgeführt.

  • Missbrauch von denormalisierten Daten. Wenn man es mit denormalisierten Daten übertreibt und versucht, sie innerhalb der Anwendung zu pflegen, ist das ein Rezept für Datenintegritätsprobleme. Verwenden Sie die Denormalisierung sparsam. Wenn Sie einer Abfrage keine Verknüpfung hinzufügen wollen, ist das keine Entschuldigung für eine Denormalisierung.

  • Angst vor dem Schreiben von SQL. SQL ist keine Raketenwissenschaft und erfüllt seine Aufgabe sogar recht gut. O/R-Mapping-Schichten sind recht gut darin, die 95 % der Abfragen zu erledigen, die einfach sind und gut in dieses Modell passen. Manchmal ist SQL der beste Weg, um eine Aufgabe zu erledigen.

  • Dogmatische "No Stored Procedures"-Politik. Unabhängig davon, ob Sie glauben, dass Stored Procedures böse sind, hat diese Art von dogmatischer Haltung in einem Softwareprojekt nichts zu suchen.

  • Sie verstehen das Datenbankdesign nicht. Die Normalisierung ist dein Freund und sie ist keine Raketenwissenschaft. Joining und Kardinalität sind recht einfache Konzepte - wenn Sie mit der Entwicklung von Datenbankanwendungen zu tun haben, gibt es eigentlich keine Entschuldigung dafür, sie nicht zu verstehen.

80voto

Rad Punkte 8279
  1. Keine Versionskontrolle für das Datenbankschema verwenden
  2. Direktes Arbeiten mit einer Live-Datenbank
  3. Keine Lektüre und kein Verständnis fortgeschrittener Datenbankkonzepte (Indizes, Cluster-Indizes, Constraints, materialisierte Ansichten usw.)
  4. Versäumnis, die Skalierbarkeit zu testen ... Testdaten von nur 3 oder 4 Zeilen geben Ihnen niemals ein wirkliches Bild der tatsächlichen Leistung

46voto

Ash Punkte 58914

Übermäßige Nutzung und/oder Abhängigkeit von gespeicherten Verfahren.

Einige Anwendungsentwickler sehen gespeicherte Prozeduren als direkte Erweiterung des Middle-Tier/Front-End-Codes. Dies scheint eine verbreitete Eigenschaft von Microsoft-Stack-Entwicklern zu sein (ich bin einer davon, aber ich habe mich davon gelöst) und führt zu vielen gespeicherten Prozeduren, die komplexe Geschäftslogik und Workflow-Verarbeitung durchführen. Dies sollte man besser woanders tun.

Gespeicherte Prozeduren sind nützlich, wenn erwiesen ist, dass ein realer technischer Faktor ihre Verwendung erforderlich macht (z. B. Leistung und Sicherheit), z. B. Aggregation/Filterung großer Datenmengen "nahe an den Daten".

Vor kurzem musste ich bei der Wartung und Verbesserung einer großen Delphi-Desktop-Anwendung helfen, bei der 70 % der Geschäftslogik und der Regeln in 1400 SQL Server Stored Procedures implementiert waren (der Rest in UI Event Handlers). Dies war ein Alptraum, vor allem wegen der Schwierigkeit, wirksame Einheitstests für TSQL einzuführen, wegen der fehlenden Kapselung und der schlechten Werkzeuge (Debugger, Editoren).

Bei meiner Arbeit in einem Java-Team habe ich schnell festgestellt, dass in dieser Umgebung oft das genaue Gegenteil der Fall ist. Ein Java-Architekt sagte mir einmal: "Die Datenbank ist für Daten da, nicht für Code.".

Heutzutage halte ich es für einen Fehler, gespeicherte Procs überhaupt nicht in Betracht zu ziehen, aber sie sollten sparsam eingesetzt werden (nicht standardmäßig) in Situationen, in denen sie nützliche Vorteile bieten (siehe die anderen Antworten).

41voto

Bob Moore Punkte 6498

Das größte Problem? Sie testen nur mit Spielzeugdatenbanken. Sie haben also keine Ahnung, dass ihr SQL nicht mehr funktioniert, wenn die Datenbank groß wird, und dass jemand kommen muss, um es später zu korrigieren (das Geräusch, das Sie hören, ist mein Zähneknirschen).

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X