Errori di sviluppo del database commessi dagli sviluppatori di applicazioni [chiuso]


566

Quali sono gli errori di sviluppo del database comuni commessi dagli sviluppatori di applicazioni?


Risposte:


1002

1. Non utilizzare indici appropriati

Questo è relativamente semplice, ma succede sempre. Le chiavi esterne dovrebbero avere indici su di esse. Se stai usando un campo in un WHERE(probabilmente) dovresti avere un indice su di esso. Tali indici dovrebbero spesso coprire più colonne in base alle query che è necessario eseguire.

2. Non applicare l'integrità referenziale

Il tuo database può variare qui, ma se il tuo database supporta l'integrità referenziale - nel senso che tutte le chiavi esterne sono garantite per puntare a un'entità esistente - dovresti usarlo.

È abbastanza comune vedere questo errore nei database MySQL. Non credo che MyISAM lo supporti. InnoDB lo fa. Troverai persone che usano MyISAM o quelle che usano InnoDB ma non lo usano comunque.

Più qui:

3. Utilizzo di chiavi primarie naturali piuttosto che surrogate (tecniche)

Le chiavi naturali sono chiavi basate su dati significativi dall'esterno (apparentemente) unici. Esempi comuni sono codici di prodotto, codici di stato a due lettere (US), numeri di previdenza sociale e così via. Le chiavi primarie surrogate o tecniche sono quelle che non hanno assolutamente alcun significato al di fuori del sistema. Sono inventati esclusivamente per identificare l'entità e sono in genere campi a incremento automatico (SQL Server, MySQL, altri) o sequenze (in particolare Oracle).

Secondo me dovresti sempre usare le chiavi surrogate. Questo problema è emerso in queste domande:

Questo è un argomento alquanto controverso sul quale non otterrai un accordo universale. Mentre potresti trovare alcune persone, che pensano che le chiavi naturali siano OK in alcune situazioni, non troverai alcuna critica alle chiavi surrogate oltre a essere probabilmente inutile. È un piccolo inconveniente se me lo chiedi.

Ricorda, anche i paesi possono smettere di esistere (ad esempio, la Jugoslavia).

4. Scrivere query che richiedono DISTINCTdi funzionare

Lo si vede spesso nelle query generate da ORM. Guarda l'output del registro da Hibernate e vedrai che tutte le query iniziano con:

SELECT DISTINCT ...

Questa è una scorciatoia per assicurarti di non restituire righe duplicate e quindi ottenere oggetti duplicati. A volte vedrai anche persone che lo fanno. Se la vedi troppo è una vera bandiera rossa. Non DISTINCTè male o non ha applicazioni valide. Lo fa (in entrambi i casi) ma non è un surrogato o un punto fermo per la scrittura di query corrette.

Da perché odio DISTINCT :

Secondo me, dove le cose iniziano a peggiorare è quando uno sviluppatore sta costruendo una query sostanziale, unendo le tabelle e all'improvviso si rende conto che sembra che stia ottenendo righe duplicate (o anche più) e la sua risposta immediata ... la sua "soluzione" a questo "problema" è di lanciare la parola chiave DISTINCT e POOF tutti i suoi problemi scompaiono .

5. Favorire l'aggregazione rispetto ai join

Un altro errore comune degli sviluppatori di applicazioni di database è quello di non rendersi conto di quanto aggregazione più costosa (ovvero la GROUP BYclausola) possa essere confrontata con i join.

Per darti un'idea di quanto questo sia diffuso, ho scritto su questo argomento diverse volte qui e sono stato molto votato per questo. Per esempio:

Dall'istruzione SQL - "join" vs "group by and have" :

Prima query:

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

Tempo di query: 0,312 s

Seconda query:

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

Tempo di query: 0,016 s

Giusto. La versione di join che ho proposto è venti volte più veloce della versione aggregata.

6. Non semplificare query complesse attraverso le viste

Non tutti i fornitori di database supportano le visualizzazioni, ma per quelli che lo fanno, possono semplificare notevolmente le query se utilizzate in modo oculato. Ad esempio, su un progetto ho usato un modello Party generico per CRM. Questa è una tecnica di modellazione estremamente potente e flessibile, ma può portare a molti join. In questo modello c'erano:

  • Partito : persone e organizzazioni;
  • Ruolo del partito : attività svolte da tali soggetti, ad esempio Dipendente e Datore di lavoro;
  • Relazione di ruolo del partito : come quei ruoli si relazionavano tra loro.

Esempio:

  • Ted è una persona, essendo un sottotipo di partito;
  • Ted ha molti ruoli, uno dei quali è Dipendente;
  • Intel è un'organizzazione, essendo un sottotipo di una Parte;
  • Intel ha molti ruoli, uno dei quali è il datore di lavoro;
  • Intel impiega Ted, il che significa che esiste una relazione tra i rispettivi ruoli.

Quindi ci sono cinque tavoli uniti per collegare Ted al suo datore di lavoro. Supponi che tutti i dipendenti siano persone (non organizzazioni) e fornisci questa visione di supporto:

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

E improvvisamente hai una visione molto semplice dei dati che desideri ma su un modello di dati altamente flessibile.

7. Ingresso non igienizzante

Questo è enorme. Ora mi piace PHP ma se non sai cosa stai facendo è davvero facile creare siti vulnerabili agli attacchi. Niente lo riassume meglio della storia dei tavolini Bobby .

I dati forniti dall'utente tramite URL, dati dei moduli e cookie devono essere sempre trattati come ostili e sterilizzati. Assicurati di ottenere quello che ti aspetti.

8. Non usare dichiarazioni preparate

Le istruzioni preparate sono quando si compila una query meno i dati utilizzati in inserti, aggiornamenti e WHEREclausole e quindi li si fornisce in seguito. Per esempio:

SELECT * FROM users WHERE username = 'bob'

vs

SELECT * FROM users WHERE username = ?

o

SELECT * FROM users WHERE username = :username

a seconda della tua piattaforma.

Ho visto database messi in ginocchio facendo questo. Fondamentalmente, ogni volta che un database moderno incontra una nuova query, deve compilarla. Se incontra una query vista in precedenza, stai offrendo al database l'opportunità di memorizzare nella cache la query compilata e il piano di esecuzione. Eseguendo la query molto, si offre al database l'opportunità di capirlo e ottimizzarlo di conseguenza (ad esempio, bloccando la query compilata in memoria).

L'uso di istruzioni preparate ti fornirà anche statistiche significative sulla frequenza con cui vengono utilizzate determinate query.

Le dichiarazioni preparate ti proteggeranno anche meglio dagli attacchi di SQL injection.

9. Non abbastanza normalizzante

La normalizzazione del database è fondamentalmente il processo di ottimizzazione della progettazione del database o di come organizzare i dati in tabelle.

Proprio questa settimana mi sono imbattuto in un codice in cui qualcuno aveva implementato un array e l'aveva inserito in un singolo campo in un database. La normalizzazione significherebbe trattare l'elemento di tale array come una riga separata in una tabella figlio (ovvero una relazione uno-a-molti).

Ciò è emerso anche nel metodo migliore per la memorizzazione di un elenco di ID utente :

Ho visto in altri sistemi che l'elenco è archiviato in un array PHP serializzato.

Ma la mancanza di normalizzazione si presenta in molte forme.

Di Più:

10. Normalizzazione troppo

Questo può sembrare una contraddizione al punto precedente ma la normalizzazione, come molte altre cose, è uno strumento. È un mezzo per un fine e non un fine in sé e per sé. Penso che molti sviluppatori lo dimentichino e inizino a considerare un "mezzo" come un "fine". Il test unitario ne è un esempio lampante.

Una volta ho lavorato su un sistema che aveva una grande gerarchia per i clienti che andava qualcosa del tipo:

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

in modo tale da dover unire circa 11 tabelle insieme prima di poter ottenere dati significativi. È stato un buon esempio di normalizzazione preso troppo lontano.

Più precisamente, un'attenta e ponderata denormalizzazione può avere enormi vantaggi in termini di prestazioni, ma devi fare molta attenzione quando lo fai.

Di Più:

11. Utilizzo di archi esclusivi

Un arco esclusivo è un errore comune in cui viene creata una tabella con due o più chiavi esterne in cui una e solo una di esse può essere non nulla. Grosso errore. Per prima cosa diventa molto più difficile mantenere l'integrità dei dati. Dopotutto, anche con integrità referenziale, nulla impedisce di impostare due o più di queste chiavi esterne (nonostante vincoli di controllo complessi).

Da una guida pratica alla progettazione di database relazionali :

Abbiamo sconsigliato vivamente la costruzione di archi esclusivi ove possibile, per la buona ragione che possono essere imbarazzanti scrivere codice e porre maggiori difficoltà di manutenzione.

12. Non eseguire affatto analisi delle prestazioni sulle query

Il pragmatismo regna sovrano, in particolare nel mondo dei database. Se stai rispettando i principi al punto che sono diventati un dogma, probabilmente hai fatto degli errori. Prendi l'esempio delle query aggregate dall'alto. La versione aggregata potrebbe sembrare "carina" ma le sue prestazioni sono deplorevoli. Un confronto tra le prestazioni avrebbe dovuto porre fine al dibattito (ma non è stato così) ma più in particolare: sputare in primo luogo tali idee poco informate è ignorante, persino pericoloso.

13. Affidamento eccessivo a UNION ALL e in particolare ai costrutti UNION

Un UNION in termini SQL concatena semplicemente insiemi di dati congruenti, il che significa che hanno lo stesso tipo e numero di colonne. La differenza tra loro è che UNION ALL è una semplice concatenazione e dovrebbe essere preferita laddove possibile mentre un'UNION farà implicitamente un DISTINCT per rimuovere le tuple duplicate.

I sindacati, come DISTINCT, hanno il loro posto. Ci sono applicazioni valide Ma se ti ritrovi a fare molti di loro, in particolare nelle sottoquery, probabilmente stai facendo qualcosa di sbagliato. Questo potrebbe essere un caso di scarsa costruzione di query o di un modello di dati mal progettato che ti costringe a fare queste cose.

Le UNION, in particolare se utilizzate in join o sottoquery dipendenti, possono paralizzare un database. Cerca di evitarli quando possibile.

14. Utilizzo delle condizioni OR nelle query

Questo potrebbe sembrare innocuo. Dopotutto, gli AND vanno bene. O dovrebbe essere OK troppo giusto? Sbagliato. Fondamentalmente una condizione AND limita il set di dati mentre una condizione OR lo cresce ma non in un modo che si presta all'ottimizzazione. Soprattutto quando le diverse condizioni OR potrebbero intersecarsi, costringendo così l'ottimizzatore a un'operazione DISTINCT sul risultato.

Cattivo:

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

Meglio:

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

Ora il tuo ottimizzatore SQL può effettivamente trasformare la prima query nella seconda. Ma potrebbe non farlo. Basta non farlo.

15. Non progettare il proprio modello di dati per prestarsi a soluzioni ad alte prestazioni

Questo è un punto difficile da quantificare. È in genere osservato dal suo effetto. Se ti ritrovi a scrivere query gnarly per attività relativamente semplici o che le query per scoprire informazioni relativamente semplici non sono efficienti, allora probabilmente hai un modello di dati scadente.

In un certo senso questo punto riassume tutti i precedenti, ma è più un ammonimento che fare cose come l'ottimizzazione delle query è spesso fatto prima quando dovrebbe essere fatto secondo. Innanzitutto è necessario assicurarsi di disporre di un buon modello di dati prima di provare a ottimizzare le prestazioni. Come ha detto Knuth:

L'ottimizzazione precoce è la radice di tutti i mali

16. Uso errato delle transazioni del database

Tutte le modifiche ai dati per un processo specifico dovrebbero essere atomiche. Cioè Se l'operazione ha esito positivo, lo fa completamente. Se fallisce, i dati rimangono invariati. - Non dovrebbero esserci possibilità di modifiche "a metà lavoro".

Idealmente, il modo più semplice per raggiungere questo obiettivo è che l'intero progetto del sistema dovrebbe sforzarsi di supportare tutte le modifiche ai dati attraverso singole istruzioni INSERT / UPDATE / DELETE. In questo caso, non è necessaria alcuna gestione speciale delle transazioni, in quanto il motore di database dovrebbe farlo automaticamente.

Tuttavia, se alcuni processi richiedono l'esecuzione di più istruzioni come unità per mantenere i dati in uno stato coerente, è necessario un adeguato controllo delle transazioni.

  • Inizia una transazione prima della prima istruzione.
  • Effettua il commit della transazione dopo l'ultima istruzione.
  • In caso di errore, ripristinare la transazione. E molto NB! Non dimenticare di saltare / interrompere tutte le istruzioni che seguono dopo l'errore.

Si raccomanda inoltre di prestare particolare attenzione alle sottigliezze di come il livello di connettività del database e il motore di database interagiscono a questo proposito.

17. Non comprendere il paradigma 'set-based'

Il linguaggio SQL segue un paradigma specifico adatto a tipi specifici di problemi. Nonostante le varie estensioni specifiche del fornitore, il linguaggio fa fatica a gestire problemi banali in lingue come Java, C #, Delphi ecc.

Questa mancanza di comprensione si manifesta in alcuni modi.

  • Imporre in modo inappropriato troppa logica procedurale o imperativa sulla banca dati.
  • Uso inappropriato o eccessivo dei cursori. Soprattutto quando basterebbe una singola query.
  • Supponendo erroneamente che il trigger attivi il fuoco una volta per riga interessato dagli aggiornamenti su più file.

Determinare una chiara divisione delle responsabilità e sforzarsi di utilizzare lo strumento appropriato per risolvere ogni problema.


9
Sulle dichiarazioni MySQL sulle chiavi esterne, hai ragione sul fatto che MyISAM non le supporta, ma implica che il semplice utilizzo di MyISAM è una cattiva progettazione. Un motivo per cui ho usato MyISAM è che InnoDB non supporta le ricerche FullText e non credo sia irragionevole.
Derek H,

1
Devo chiedere di # 6. Usare viste come questa è una delle mie cose preferite da fare, ma recentemente ho imparato, con mio orrore, che con gli indici MySQL sulle tabelle sottostanti vengono rispettate solo se la struttura della vista consente l'uso dell'algoritmo di unione. Altrimenti, viene utilizzata una tabella temporanea e tutti gli indici sono inutili. È ancora più allarmante quando ti rendi conto che un sacco di operazioni causano questo comportamento. È un ottimo modo per trasformare una query di 0,01 secondi in una query di 100 secondi. Qualcun altro qui ha esperienza con questo? Controlla i link nel mio prossimo commento.
Peter Bailey,

5
Completamente in disaccordo con # 3. Sì, i paesi possono smettere di esistere, ma il codice paese continuerà a rappresentare la stessa cosa. Lo stesso vale per i codici valuta o gli Stati Uniti. In questi casi è stupido utilizzare una chiave surrogata e creare un overhead maggiore nelle query poiché è necessario includere un join aggiuntivo. Direi che è più sicuro dire che probabilmente dovresti usare un surrogato per dati specifici dell'utente (quindi, non paesi, valute e Stati Uniti).
Thomas,

1
RE: # 11 Il vincolo di controllo necessario per imporre l'integrità dei dati è banale. Esistono altri motivi per evitare tale progetto, ma la necessità di vincoli di controllo "complessi" non è uno di questi.
Thomas,

2
Con # 3 non sei onesto. Ci sono più aspetti negativi della chiave artificiale che "potresti non averne bisogno". In particolare, l'utilizzo di una chiave naturale ti darà la possibilità di controllare l'ordine in cui i dati della tabella vengono scritti sul disco. Se sai come verrà interrogata la tua tabella, puoi indicizzarla in modo che le righe con accesso simultaneo finiscano nella stessa pagina. Inoltre, è possibile applicare l'integrità dei dati utilizzando un indice composito univoco. Se ne hai bisogno, dovrai aggiungerlo in aggiunta all'indice della chiave artificiale. Se detto indice composito è la tua scimmia, sono 2 uccelli uccisi con una fava.
Shane H,

110

Principali errori di progettazione e programmazione del database commessi dagli sviluppatori

  • Progettazione e utilizzo di database egoistici. Gli sviluppatori spesso trattano il database come un archivio oggetti persistente personale senza considerare le esigenze di altri stakeholder nei dati. Questo vale anche per gli architetti delle applicazioni. La cattiva progettazione del database e l'integrità dei dati rendono difficile per i terzi lavorare con i dati e può aumentare notevolmente i costi del ciclo di vita del sistema. Reporting e MIS tendono ad essere un povero cugino nella progettazione dell'applicazione e fatto solo come ripensamento.

  • Abuso di dati denormalizzati. Esagerare con i dati denormalizzati e cercare di mantenerli all'interno dell'applicazione è una ricetta per i problemi di integrità dei dati. Usa la denormalizzazione con parsimonia. Non voler aggiungere un join a una query non è una scusa per denormalizzare.

  • Paura di scrivere SQL. SQL non è scienza missilistica ed è in realtà abbastanza bravo a fare il suo lavoro. I layer di mappatura O / R sono abbastanza bravi a fare il 95% delle query che sono semplici e si adattano bene a quel modello. A volte SQL è il modo migliore per fare il lavoro.

  • Politiche dogmatiche "Nessuna procedura memorizzata". Indipendentemente dal fatto che tu creda che le procedure memorizzate siano malvagie, questo tipo di atteggiamento dogmatico non ha spazio su un progetto software.

  • Non capire la progettazione del database. La normalizzazione è tua amica e non è scienza missilistica. Partecipare e cardinalità sono concetti abbastanza semplici: se sei coinvolto nello sviluppo di applicazioni di database non ci sono davvero scuse per non capirle.


2
Si potrebbe sostenere che le transazioni dovrebbero essere eseguite nel database transazionale e nel reporting e che il MIS dovrebbe essere eseguito in un database di analisi separato. Quindi ottieni il meglio da entrambi i mondi e tutti sono felici (tranne il povero mug che deve scrivere lo script di trasformazione dei dati per costruire il secondo dal primo).
Chris Simpson,

Non solo la povera tazza che scrive l'ETL - chiunque utilizzi i dati dal sistema, i dati di scarsa qualità nell'applicazione MIS che è racchiusa perché molte relazioni chiave non sono effettivamente registrate alla fonte, chiunque sia coinvolto nelle interminabili bunker di riconciliazione che ne conseguono dalla scarsa qualità dei dati.
ConcernedOfTunbridgeWells il

Non potrei essere più in disaccordo con il punto uno. I database sono per la persistenza, non per la comunicazione tra processi. Esistono quasi sempre soluzioni migliori a questo problema. A meno che non vi sia un requisito esplicito per esso, si DEVE assolutamente trattare il database come se nessuno, tranne la propria applicazione, lo userà mai. Anche se esiste un requisito esplicito, fai alcune storie utente e analisi della causa principale su di esso e scoprirai abbastanza spesso un modo molto migliore di riempire l'intento del richiedente. Inoltre, lavoro in un'azienda in cui la frase CQRS è piuttosto comune
George Mauer,

3
Esempio fondamentale: ho un sistema di amministrazione della polizza assicurativa e devo calcolare lo stato di 5 milioni di sinistri in un sistema di riassicurazione ceduto per calcolare i potenziali recuperi. I sistemi sono pacchetti COTS client-server meno recenti, progettati per interfacciarsi con sistemi mainframe ancora più vecchi. Entrambi devono essere riconciliati ai fini del controllo finanziario. Questo lavoro viene svolto una volta al mese. Secondo la tua logica, scriverei una serie di storie utente che definiscono i requisiti e chiederei ai fornitori di citare l'aggiunta di un wrapper di servizi Web ai loro prodotti esistenti.
ConcernedOfTunbridgeWells

2
Quindi il tuo DBA è pigro o incompetente.
ConcernedOfTunbridgeWells

80
  1. Non utilizzare il controllo versione sullo schema del database
  2. Lavorare direttamente su un database live
  3. Non leggere e comprendere concetti di database più avanzati (indici, indici cluster, vincoli, viste materializzate, ecc.)
  4. Impossibile verificare la scalabilità ... I dati di test di sole 3 o 4 righe non ti daranno mai il quadro reale delle prestazioni dal vivo reali

1
Secondo, pesantemente, # 1 e # 2. Ogni volta che apporto una modifica al DB, ne scarico lo schema e lo versione; Ho installato tre database, uno di sviluppo, uno di gestione temporanea e uno live - NIENTE viene mai "testato" sul database live !!
Ixmatus,

Qui a Red Gate abbiamo preso provvedimenti per migliorare il tuo primo punto con SQL Source Control! Dalle conversazioni che ho avuto durante la mia ricerca, penso che le persone non stiano più sviluppando contro i database di produzione, ma spesso vengono apportate correzioni "di emergenza" che generalmente trovano la strada per tornare agli ambienti di sviluppo, che è un altro problema.
David Atkinson,

46

Uso eccessivo e / o dipendenza dalle procedure memorizzate.

Alcuni sviluppatori di applicazioni vedono le stored procedure come un'estensione diretta del codice di livello intermedio / front-end. Questo sembra essere un tratto comune negli sviluppatori di stack Microsoft (ne sono uno, ma ne sono cresciuto) e produce molte procedure memorizzate che eseguono complesse logiche aziendali ed elaborazione del flusso di lavoro. Questo è molto meglio fatto altrove.

Le procedure memorizzate sono utili laddove è stato effettivamente dimostrato che alcuni reali fattori tecnici ne richiedono l'uso (ad esempio, prestazioni e sicurezza) Ad esempio, mantenendo l'aggregazione / filtro di grandi insiemi di dati "vicini ai dati".

Di recente ho dovuto aiutare a mantenere e migliorare una grande applicazione desktop Delphi di cui il 70% della logica aziendale e delle regole sono state implementate in 1400 procedure memorizzate di SQL Server (il resto nei gestori di eventi dell'interfaccia utente). Questo è stato un incubo, principalmente a causa della difficoltà di introdurre efficaci test unitari su TSQL, mancanza di incapsulamento e strumenti scadenti (debugger, editor).

In passato, lavorando con un team Java ho scoperto rapidamente che l'opposto completo vale in quell'ambiente. Un architetto Java una volta mi disse: "Il database è per i dati, non per il codice".

In questi giorni penso che sia un errore non considerare affatto i proc memorizzati, ma dovrebbero essere usati con parsimonia (non di default) in situazioni in cui forniscono vantaggi utili (vedi le altre risposte).


4
Le procedure memorizzate tendono a diventare un'isola di danno in qualsiasi progetto in cui vengono utilizzate, quindi alcuni sviluppatori definiscono una regola "Nessuna procedura memorizzata". Quindi sembra che ci sia un conflitto aperto tra loro. La tua risposta è un buon esempio per quando effettivamente scegliere in un modo o nell'altro.
Warren P

Vantaggi: sicurezza - non è necessario dare alle applicazioni la possibilità di "eliminare * da ..."; tweaks: i DBA possono modificare le query senza dover ricompilare / distribuire l'intera applicazione; analisi - è facile ricompilare un sacco di proc dopo una modifica del modello di dati per assicurarsi che siano ancora validi; e, infine, considerando che SQL viene eseguito dal motore di database (non dall'applicazione), il concetto di "database è per dati, non per codice" viene semplicemente ritardato.
NotMe,

Quindi, intrappoleresti la tua logica di business nell'interfaccia utente, dove è stata separata dai dati manipolati? Questa non sembra una buona idea, in particolare perché la manipolazione dei dati è più efficiente se eseguita dal server di database anziché dai round trip dall'interfaccia utente. Ciò significa anche che è più difficile controllare l'applicazione perché non è possibile fare affidamento sul fatto che il database abbia il controllo dei suoi dati e che potenzialmente dispongano di versioni diverse di un'interfaccia utente con manipolazione dei dati diversa in corso. Non bene. Non lascio che nulla tocchi i miei dati se non attraverso una procedura memorizzata.
David T. Macknet,

Se è necessario separare la logica aziendale dall'interfaccia utente, è possibile utilizzare architetture multilivello. Oppure, una libreria con oggetti business e logica, utilizzata da diverse app / UI. Le procedure memorizzate bloccano i dati / la logica aziendale su un database specifico, la modifica di un database in questo caso è molto costosa. E il costo enorme è cattivo.
troppo

@too: la modifica di un database nella maggior parte dei casi è molto costosa. Non dimenticare l'idea di perdere le prestazioni e le funzionalità di sicurezza fornite da un determinato DBMS. Inoltre, livelli aggiuntivi aggiungono complessità e diminuiscono le prestazioni e livelli aggiuntivi sono legati alla tua lingua particolare. Infine, è più probabile che la lingua utilizzata cambierà rispetto a un server di database.
NotMe

41

Problema numero uno? Testano solo su database di giocattoli. Quindi non hanno idea che il loro SQL eseguirà la scansione quando il database diventa grande e qualcuno deve presentarsi e risolverlo in seguito (quel suono che puoi sentire è il digrignamento dei miei denti).


2
Le dimensioni del database sono rilevanti, ma si verifica un problema maggiore: anche se si esegue il test su un set di dati reale, non si stanno testando le prestazioni delle query quando il database è sotto un carico di produzione, il che può rivelarsi un vero toccasana.
David

Direi che la dimensione del database è un problema maggiore rispetto al caricamento. Ho visto molte volte che mancavano indici cruciali - mai problemi di prestazioni durante i test, perché l'intero database si adattava alla memoria
Danubian Sailor,


28

Scarse prestazioni causate da sottoquery correlate

Il più delle volte si desidera evitare sottoquery correlate. Una sottoquery è correlata se, all'interno della sottoquery, esiste un riferimento a una colonna della query esterna. In questo caso, la sottoquery viene eseguita almeno una volta per ogni riga restituita e potrebbe essere eseguita più volte se vengono applicate altre condizioni dopo l'applicazione della condizione contenente la sottoquery correlata.

Perdona l'esempio inventato e la sintassi Oracle, ma diciamo che volevi trovare tutti i dipendenti che sono stati assunti in uno dei tuoi negozi dall'ultima volta che il negozio ha effettuato meno di $ 10.000 di vendite in un giorno.

select e.first_name, e.last_name
from employee e
where e.start_date > 
        (select max(ds.transaction_date)
         from daily_sales ds
         where ds.store_id = e.store_id and
               ds.total < 10000)

La sottoquery in questo esempio è correlata alla query esterna da store_id e verrebbe eseguita per ogni dipendente nel sistema. Un modo per ottimizzare questa query è spostare la sottoquery in una vista incorporata.

select e.first_name, e.last_name
from employee e,
     (select ds.store_id,
             max(s.transaction_date) transaction_date
      from daily_sales ds
      where ds.total < 10000
      group by s.store_id) dsx
where e.store_id = dsx.store_id and
      e.start_date > dsx.transaction_date

In questo esempio, la query nella clausola from è ora una vista inline (di nuovo una sintassi specifica di Oracle) e viene eseguita una sola volta. A seconda del modello di dati, questa query verrà probabilmente eseguita molto più velocemente. Sarebbe meglio della prima query man mano che cresceva il numero di dipendenti. La prima query potrebbe effettivamente funzionare meglio se c'erano pochi dipendenti e molti negozi (e forse molti negozi non avevano dipendenti) e la tabella daily_sales veniva indicizzata su store_id. Questo non è uno scenario probabile ma mostra come una query correlata potrebbe funzionare meglio di un'alternativa.

Ho visto molte volte gli sviluppatori junior mettere in relazione le subquery e di solito ha avuto un forte impatto sulle prestazioni. Tuttavia, quando si rimuove una sottoquery correlata, assicurarsi di consultare il piano esplicativo prima e dopo per assicurarsi di non peggiorare le prestazioni.


1
Ottimo punto e enfatizzare uno dei tuoi punti correlati: prova le tue modifiche. Impara a usare i piani di spiegazione (e vedi cosa sta facendo il database per eseguire la tua query e quanto costa), fai i test su un set di dati di grandi dimensioni e non rendere il tuo SQL eccessivamente complesso e illeggibile / non mantenibile per un'ottimizzazione che in realtà non migliora le prestazioni reali.
Rob Whelan il

21

Nella mia esperienza:
non comunicare con DBA esperti.


17

Utilizzo di Access anziché di un database "reale". Ci sono molti grandi database piccoli e persino gratuiti come SQL Express , MySQL e SQLite che funzioneranno e scaleranno molto meglio. Le app spesso devono ridimensionarsi in modi inaspettati.


16

Dimenticando di stabilire relazioni tra le tabelle. Ricordo di aver dovuto ripulirlo quando ho iniziato a lavorare presso il mio attuale datore di lavoro.


14

Utilizzo di Excel per l'archiviazione (enormi quantità di) dati.

Ho visto aziende con migliaia di righe e utilizzo di più fogli di lavoro (a causa del limite di righe di 65535 nelle versioni precedenti di Excel).


Excel è adatto per report, presentazione dei dati e altre attività, ma non deve essere trattato come un database.


14

Vorrei aggiungere: Favorire il codice "Elegante" rispetto al codice altamente performante. Il codice che funziona meglio con i database è spesso brutto agli occhi dello sviluppatore dell'applicazione.

Credere a queste sciocchezze sull'ottimizzazione prematura. I database devono considerare le prestazioni nella progettazione originale e in ogni successivo sviluppo. Le prestazioni sono il 50% della progettazione del database (il 40% è l'integrità dei dati e l'ultimo 10% è la sicurezza) secondo me. I database che non sono costruiti dal basso verso l'alto funzioneranno male una volta che utenti reali e traffico reale sono posizionati sul database. L'ottimizzazione prematura non significa nessuna ottimizzazione! Ciò non significa che dovresti scrivere codice che funzionerà quasi sempre male perché lo trovi più facile (ad esempio i cursori che non dovrebbero mai essere consentiti in un database di produzione a meno che tutto il resto non sia fallito). Significa che non è necessario guardare a spremere l'ultimo po 'di prestazioni fino a quando non è necessario. Si sa molto su ciò che funzionerà meglio sui database,


2
+1 - La programmazione del database comporta l'ottimizzazione del comportamento dei componenti meccanici. Nota, tuttavia, che Knuth afferma che l'ottimizzazione prematura è la radice di tutti i mali circa il 97% delle volte (o parole in tal senso). Il design del database è un'area in cui devi davvero pensarci in anticipo.
Preoccupato di

2
Ahem ... di cosa stai parlando è l'ottimizzazione che non è prematura. Alcune considerazioni sull'uso reale sono necessarie sin dall'inizio nella progettazione di database (e anche nella progettazione di applicazioni). La regola di Knuth in realtà non è banale da seguire, perché devi decidere cosa è prematuro e cosa non lo è - si riduce davvero a "non eseguire ottimizzazioni senza dati". Le prime decisioni relative alle prestazioni di cui parli hanno dati: alcuni progetti imposteranno limiti inaccettabili sulle prestazioni future e puoi calcolarli.
Rob Whelan il

13

Non utilizzare query con parametri. Sono abbastanza utili per fermare SQL Injection .

Questo è un esempio specifico di non sanificazione dei dati di input, menzionato in un'altra risposta.


3
Tranne l'input di sanificazione è sbagliato. La sanificazione implica metterlo da qualche parte dove può essere pericoloso. Parametrizzare significa tenerlo completamente fuori dalla strada del danno.
Dustin,

12

Lo odio quando gli sviluppatori usano le istruzioni select nidificate o addirittura le funzioni restituiscono il risultato di un'istruzione select all'interno della parte "SELECT" di una query.

In realtà sono sorpreso di non vederlo da nessun'altra parte qui, forse l'ho trascurato, anche se @adam ha indicato un problema simile.

Esempio:

SELECT
    (SELECT TOP 1 SomeValue FROM SomeTable WHERE SomeDate = c.Date ORDER BY SomeValue desc) As FirstVal
    ,(SELECT OtherValue FROM SomeOtherTable WHERE SomeOtherCriteria = c.Criteria) As SecondVal
FROM
    MyTable c

In questo scenario, se MyTable restituisce 10000 righe, il risultato è come se la query avesse appena eseguito 20001 query, poiché doveva eseguire la query iniziale più la query su ciascuna delle altre tabelle una volta per ogni riga di risultato.

Gli sviluppatori possono cavarsela lavorando in un ambiente di sviluppo in cui restituiscono solo poche righe di dati e le tabelle secondarie di solito contengono solo una piccola quantità di dati, ma in un ambiente di produzione questo tipo di query può diventare esponenzialmente costosa quanto più i dati vengono aggiunti alle tabelle.

Un esempio migliore (non necessariamente perfetto) sarebbe qualcosa di simile:

SELECT
     s.SomeValue As FirstVal
    ,o.OtherValue As SecondVal
FROM
    MyTable c
    LEFT JOIN (
        SELECT SomeDate, MAX(SomeValue) as SomeValue
        FROM SomeTable 
        GROUP BY SomeDate
     ) s ON c.Date = s.SomeDate
    LEFT JOIN SomeOtherTable o ON c.Criteria = o.SomeOtherCriteria

Ciò consente agli ottimizzatori del database di mescolare i dati insieme, anziché la richiesta su ogni record della tabella principale e di solito trovo che quando devo correggere il codice in cui è stato creato questo problema, di solito finisco per aumentare la velocità delle query del 100% o più riducendo contemporaneamente l'utilizzo della CPU e della memoria.


12

Per database basati su SQL:

  1. Non approfittare degli INDICI CLUSTER o scegliere le colonne sbagliate su CLUSTER.
  2. Non utilizzare un tipo di dati SERIAL (autonumber) come PRIMARY KEY per unirsi a un FOREIGN KEY (INT) in una relazione di tabella padre / figlio.
  3. Non AGGIORNAMENTO DELLE STATISTICHE su una tabella quando molti record sono stati INSERITI o ELIMINATI.
  4. Non riorganizzare (ovvero scaricare, eliminare, ricreare, caricare e reindicizzare) le tabelle quando sono state inserite o eliminate molte righe (alcuni motori mantengono fisicamente le righe eliminate in una tabella con un flag di eliminazione).
  5. Non sfruttare FRAMMENTO IN ESPRESSIONE (se supportato) su tabelle di grandi dimensioni con tassi di transazione elevati.
  6. Scegliere il tipo di dati errato per una colonna!
  7. Non scegliere un nome di colonna appropriato.
  8. Non aggiungere nuove colonne alla fine della tabella.
  9. Non creare indici adeguati per supportare le query utilizzate di frequente.
  10. creazione di indici su colonne con pochi valori possibili e creazione di indici non necessari.
    ... altro da aggiungere.

1
Un cavillo: 2) è in realtà una cattiva pratica. Vedo che cosa stai ottenendo: vuoi un indice univoco su quel numero automatico e utilizzarlo come chiave surrogata. Ma la chiave primaria non dovrebbe essere un numero automatico, in quanto non è ciò che è una chiave primaria: una chiave primaria è "ciò di cui parla il record", che (ad eccezione di cose come le transazioni di vendita) NON è il numero automatico, ma un po 'univoco di informazioni sull'entità da modellare.
David T. Macknet,

il motivo principale dell'utilizzo di autonumber per chiave primaria ed esterna è garantire che un join padre-figlio possa essere mantenuto indipendentemente dalle modifiche in qualsiasi altra colonna. l'utilizzo di una chiave primaria diversa, come il nome del cliente o altri dati, può essere rischioso!
Frank R.

@David: sto corretto! .. non è necessario utilizzare autonumber come chiave primaria, si può ancora avere una colonna seriale indicizzata nel genitore, unendo il surrogato nel figlio per garantire che la relazione non venga interrotta, pur avendo un altro colonna come primario significativo per individuare la riga!
Frank R.

È un problema di semantica, alla fine della giornata ... e Microsoft preferisce che le chiavi primarie siano prive di significato, piuttosto che significative. I dibattiti al riguardo infuriano, ma cado nel campo "significativo". :)
David T. Macknet,

9
  • Non eseguire un backup prima di risolvere alcuni problemi all'interno del database di produzione.

  • Utilizzo dei comandi DDL su oggetti memorizzati (come tabelle, viste) nelle procedure memorizzate.

  • Paura di usare proc memorizzati o paura di usare query ORM ovunque sia più efficiente / appropriato da usare.

  • Ignorando l'uso di un profiler di database, che può dirti esattamente in cosa viene convertita la tua query ORM e quindi verificare la logica o persino il debug quando non si utilizza ORM.


8

Non fare il corretto livello di normalizzazione . Si desidera assicurarsi che i dati non siano duplicati e che si stiano dividendo i dati in diversi secondo necessità. È inoltre necessario assicurarsi di non seguire troppo la normalizzazione poiché ciò pregiudicherebbe le prestazioni.


Quanto è troppo lontano? Se nessun dato viene duplicato, come è possibile approfondirlo?
Finnw

La normalizzazione è un equilibrio tra la rimozione di dati ridondanti e l'aumento della flessibilità rispetto alla riduzione delle prestazioni e alla maggiore complessità. Trovare il giusto equilibrio richiede esperienza e cambia nel tempo. Vedi en.wikipedia.org/wiki/Database_normalization per informazioni su quando denormalizzare
Nathan Voxland

8

Trattare il database come un semplice meccanismo di archiviazione (ovvero libreria di raccolte glorificate) e quindi subordinato alla loro applicazione (ignorando altre applicazioni che condividono i dati)


Un corollario di ciò sta scaricando troppo lavoro di query sull'applicazione invece di tenerlo nel db a cui appartiene. LINQ è particolarmente cattivo al riguardo.
3Daveva il

8
  • Respingere un ORM come Hibernate fuori mano, per ragioni come "è troppo magico" o "non sul mio database".
  • Fare troppo affidamento su un ORM come Hibernate e cercare di farlo calpestare dove non è appropriato.

8

1 - Uso non necessario di una funzione su un valore in una clausola where con il risultato di quell'indice non utilizzato.

Esempio:

where to_char(someDate,'YYYYMMDD') between :fromDate and :toDate

invece di

where someDate >= to_date(:fromDate,'YYYYMMDD') and someDate < to_date(:toDate,'YYYYMMDD')+1

E in misura minore: non aggiungere indici funzionali a quei valori che ne hanno bisogno ...

2 - Non aggiungere vincoli di controllo per garantire la validità dei dati. I vincoli possono essere utilizzati da Query Optimizer e aiutano DAVVERO a garantire la fiducia dei propri invarianti. Non c'è motivo di non usarli.

3 - Aggiunta di colonne non normalizzate alle tabelle per pura pigrizia o pressione del tempo. Le cose di solito non sono progettate in questo modo, ma si evolvono in questo. Il risultato finale, senza fallo, è un sacco di lavoro che cerca di ripulire il casino quando sei morso dalla perdita di integrità dei dati nelle future evoluzioni.

Pensa a questo, una tabella senza dati è molto economica da ridisegnare. Una tabella con un paio di milioni di record senza integrità ... non così economica da riprogettare. Pertanto, la progettazione corretta durante la creazione della colonna o della tabella viene ammortizzata in picche.

4 - non tanto sul database in sé, ma in effetti fastidioso. Non preoccuparsi della qualità del codice di SQL. Il fatto che il tuo SQL sia espresso in testo non rende OK nascondere la logica in un mucchio di algoritmi di manipolazione delle stringhe. È perfettamente possibile scrivere SQL nel testo in un modo che sia effettivamente leggibile dal tuo collega programmatore.


7

Questo è stato detto prima, ma: indici, indici, indici . Ho visto così tanti casi di app Web aziendali con prestazioni scarse che sono state risolte semplicemente facendo un po 'di profilazione (per vedere quali tabelle venivano colpite molto) e quindi aggiungendo un indice su quelle tabelle. Questo non richiede nemmeno molto in termini di conoscenza della scrittura SQL e il payoff è enorme.

Evita la duplicazione dei dati come la peste. Alcune persone sostengono che una piccola duplicazione non farà male e migliorerà le prestazioni. Ehi, non sto dicendo che devi torturare il tuo schema in Third Normal Form, fino a quando non è così astratto che nemmeno i DBA sanno cosa sta succedendo. Basta capire che ogni volta che si duplica una serie di nomi, codici postali o codici di spedizione, le copie non si sincronizzeranno tra loro alla fine. Succederà. E poi ti prenderai a calci mentre esegui lo script di manutenzione settimanale.

E infine: utilizzare una convenzione di denominazione chiara, coerente e intuitiva. Allo stesso modo in cui un pezzo di codice ben scritto dovrebbe essere leggibile, un buon schema o query SQL dovrebbe essere leggibile e praticamente dirti cosa sta facendo, anche senza commenti. Ti ringrazierai tra sei mesi, quando dovrai fare manutenzione sui tavoli. "SELECT account_number, billing_date FROM national_accounts"è infinitamente più facile da lavorare rispetto a "SELEZIONA ACCNTNBR, BILLDAT DA NTNLACCTS".


Se li configuri correttamente, non lo faranno, ma ciò comporta l'uso di trigger a cui molte persone sono allergiche.
HLGEM,

6

Non eseguire una query SELECT corrispondente prima di eseguire la query DELETE (in particolare sui database di produzione)!


5

L'errore più comune che ho visto in vent'anni: non pianificare in anticipo. Molti sviluppatori creeranno un database e delle tabelle, quindi modificheranno ed espanderanno continuamente le tabelle durante la creazione delle applicazioni. Il risultato finale è spesso un disastro e inefficiente e difficile da ripulire o semplificare in seguito.


1
Posso immaginare gli orrori che ne conseguono in queste situazioni ... I database Schemaless si adattano molto meglio alla prototipazione rapida e allo sviluppo iterativo, ma come tutto il resto, tale flessibilità comporta vari compromessi.
Zsolt Török,

4

a) Valori della query di hardcoding nella stringa
b) Inserimento del codice di query del database nell'azione "OnButtonPress" in un'applicazione Windows Form

Ho visto entrambi.


4
"Inserimento del codice query DB nell'azione" OnButtonPress "in un'applicazione Windows Form" Qual è l'errore del database qui?
ricorsivo

@recursive: è un'enorme vulnerabilità nell'iniezione SQL. Chiunque può inviare SQL arbitrario al server e verrà eseguito alla lettera.
Bill Karwin,

Concordato con @recursive. Questi non hanno davvero nulla a che fare con i problemi di DB.
Campbell

b) è un errore di architettura. Ovviamente, le query di codifica direttamente nella tua app sono comunque una cattiva idea.
3Daveva il

4

Non prestando sufficiente attenzione alla gestione delle connessioni al database nella tua applicazione. Quindi scopri che l'applicazione, il computer, il server e la rete sono intasati.


4
  1. Pensando che siano DBA e modellatori / progettisti di dati quando non hanno indottrinamento formale di alcun tipo in quelle aree.

  2. Pensare che il loro progetto non richiede un DBA perché quella roba è tutto facile / banale.

  3. Impossibile distinguere correttamente tra il lavoro che dovrebbe essere fatto nel database e il lavoro che dovrebbe essere fatto nell'app.

  4. Non convalidare i backup o non eseguire il backup.

  5. Incorporare SQL raw nel loro codice.



3

Non avere una comprensione del modello di concorrenza dei database e di come ciò influisca sullo sviluppo. È facile aggiungere indici e modificare le query dopo il fatto. Tuttavia, le applicazioni progettate senza la dovuta considerazione di hotspot, contesa di risorse e funzionamento corretto (supponendo che ciò che hai appena letto sia ancora valido!) Possono richiedere modifiche significative all'interno del database e del livello dell'applicazione per correggerle in seguito.


3

Non capire come funziona un DBMS sotto il cofano.

Non puoi guidare correttamente una levetta senza capire come funziona una frizione. E non puoi capire come usare un database senza capire che stai davvero scrivendo su un file sul tuo disco rigido.

In particolare:

  1. Sai cos'è un indice cluster? Ci hai pensato quando hai progettato il tuo schema?

  2. Sai come usare correttamente gli indici? Come riutilizzare un indice? Sai cos'è un indice di copertura?

  3. Così fantastico, hai degli indici. Quanto è grande 1 riga nel tuo indice? Quanto sarà grande l'indice quando avrai molti dati? Si adatterà facilmente alla memoria? Altrimenti è inutile come indice.

  4. Hai mai usato EXPLAIN in MySQL? Grande. Ora sii onesto con te stesso: hai capito anche la metà di ciò che hai visto? No, probabilmente non l'hai fatto. Risolvilo.

  5. Capisci la cache delle query? Sai cosa rende inaccettabile una query?

  6. Stai usando MyISAM? Se hai BISOGNO della ricerca a testo integrale, MyISAM è comunque una schifezza. Usa la Sfinge. Quindi passare a Inno.


2
Un'analogia migliore potrebbe essere che non è possibile risolvere correttamente una trasmissione manuale senza comprendere una frizione. Molte persone guidano correttamente un cambio di marcia senza sapere come funziona una frizione.
Michael Easter,

3
  1. Utilizzo di un ORM per eseguire aggiornamenti in blocco
  2. Selezione di più dati del necessario. Ancora una volta, in genere quando si utilizza un ORM
  3. Sparatutto in loop.
  4. Non avere buoni dati di test e notare il degrado delle prestazioni solo sui dati live.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.