"Non fare mai nel codice ciò che si può ottenere per fare in modo che il server SQL funzioni bene per te" - Questa è una ricetta per un cattivo design?

193

È un'idea che ho sentito ripetere in una manciata di posti. Alcuni riconoscono più o meno che, una volta che si tenta di risolvere un problema puramente in SQL, si supera un certo livello di complessità, si dovrebbe infatti gestirlo nel codice.

La logica dietro l'idea è che per la maggior parte dei casi, il motore di database svolgerà un lavoro migliore nel trovare il modo più efficiente di completare il tuo compito rispetto a quello che potresti fare nel codice. Soprattutto quando si tratta di cose come rendere i risultati condizionali sulle operazioni eseguite sui dati. Probabilmente con i motori moderni in modo efficace JIT'ing + memorizzando nella cache la versione compilata della tua query avrebbe senso sulla superficie.

La domanda è se sfruttare il tuo motore di database in questo modo è una pratica di progettazione intrinsecamente negativa (e perché). Le linee si offuscano ulteriormente quando tutta la logica esiste all'interno del database e la stai solo colpendo tramite un ORM.

    
posta PhonicUK 22.03.2014 - 15:16
fonte

14 risposte

312

In parole semplici:

Queste sono cose che SQL è fatto per fare e, che ci crediate o no, ho visto fare nel codice:

  • join - in codice richiederebbe una complessa manipolazione dell'array
  • dati di filtraggio (dove) - in codice richiederebbe un pesante inserimento e cancellazione di elementi negli elenchi
  • selezionare le colonne - in codice richiederebbe una lista pesante o la manipolazione della schiera
  • funzioni di aggregazione - in codice richiederebbero matrici per contenere valori e complessi casi di switch
  • integrità della chiave esterna - codewise richiede query prima dell'inserimento e presuppone che nessuno utilizzi i dati all'esterno dell'app
  • integrità della chiave primaria - codewise richiede query prima dell'inserimento e presuppone che nessuno utilizzi i dati all'esterno dell'app

Fare queste cose invece di affidarsi a SQL o RDBMS porta a scrivere tonnellate di codice senza alcun valore aggiunto , il che significa più codice da eseguire per il debug e la manutenzione. E si presume che il database sia accessibile solo tramite l'applicazione.

    
risposta data 22.07.2013 - 18:40
fonte
120

Vorrei riformulare quello per "Non fare mai nel codice ciò che SQL Server può fare per te bene ".

Cose come la manipolazione delle stringhe, il lavoro regex e cose che non farei in SQL Server (con esclusione di SQL CLR).

Quanto sopra tende a parlare di cose come: join, set operations e query. L'intenzione è quella di delegare gran parte del pesante sollevamento a SQL Server (a cose è buono) e ridurre la quantità di I / O il più possibile (in modo che SQL esegua i join e filtri con una clausola WHERE , restituendo un set di dati molto più piccolo rispetto al contrario).

    
risposta data 23.10.2012 - 17:35
fonte
46

Never do in code what you can get the SQL server to do well for you (emphasis is mine)

La chiave della risposta è che devi cercare SQL per fare qualcosa di buono, invece di fare semplicemente qualcosa per te. SQL è un linguaggio incredibilmente potente. Accoppiato con funzioni built-in, può potenzialmente fare un sacco di cose. Tuttavia, il fatto che tu possa fare qualcosa in SQL non dovrebbe essere una scusa per farlo realmente in SQL.

I miei criteri specifici per prendere una decisione è guardare la quantità di dati che si ottiene e il numero di round-trip: se si può tagliare la quantità di dati spedendo un'attività al server, senza aumentare il numero di round-trip, quindi l'attività appartiene al server; se la quantità di dati rimane la stessa o aumenta senza una diminuzione simultanea del numero di round trip, l'attività appartiene al tuo codice.

Considera questi esempi:

  • Memorizzi una data di nascita e devi calcolare l'età per un gruppo di utenti. Puoi fare in modo che il server SQL esegua la sottrazione, oppure puoi farlo nel tuo codice. Il numero di round trip rimane lo stesso e la quantità di dati inviati all'utente sale. Pertanto, una soluzione basata su codice vince
  • Memorizzi una data di nascita e devi trovare utenti di età compresa tra 20 e 30. Puoi caricare tutti gli utenti sul client, eseguire la sottrazione per trovare l'età, quindi filtrare, ma spedire la logica a SQL Server ridurrebbe la quantità di dati senza richiedere ulteriori round trip; pertanto, la soluzione basata su SQL vince.
risposta data 23.10.2012 - 18:03
fonte
20

In breve , sarebbe corretto dire che: "Non eseguire mai operazioni specifiche del database nella tua base di codice" poiché sono migliori indirizzato nel tuo database.

Guarda l'esempio degli imposta le operazioni di base . Come forse saprai, RDBMS sono stati creati per gestire operazioni di archiviazione e manipolazione dei dati comuni.

Inoltre, la scelta del database del progetto ha un ruolo importante . Avere un RDBMS (MS SQL, Oracle, ecc.) È diverso dai database NoSQL come RavenDB.

    
risposta data 24.10.2012 - 18:15
fonte
13

Di norma, il DB contiene più informazioni su cui lavorare rispetto all'applicazione e può eseguire operazioni di dati comuni in modo più efficiente. Il tuo database mantiene indici, ad esempio, mentre l'applicazione dovrebbe indicizzare i risultati di ricerca al volo. A parità di tutti gli altri, il carico di lavoro complessivo può essere ridotto spingendo il lavoro al database anziché all'applicazione.

Tuttavia, poiché il tuo prodotto si ridimensiona, in genere diventa più semplice scalare la tua app che scalare il tuo db. Nelle installazioni di grandi dimensioni, non è raro vedere i server di applicazioni più numerosi dei server di database di un fattore da 10 a 1 o più. Aggiungere più server applicazioni è spesso una semplice questione di clonazione di un server esistente sul nuovo hardware. L'aggiunta di nuovi server di database, d'altra parte, è drammaticamente più difficile nella maggior parte dei casi.

Quindi, a questo punto, il mantra diventa protegge il database . Si scopre che memorizzando nella cache i risultati del database in memcached o accodando gli aggiornamenti in un registro lato applicazione, o recuperando i dati una volta e calcolando le statistiche nella tua app, puoi ridurre drasticamente il carico di lavoro del database, risparmiandoti di avere per ricorrere a una configurazione cluster DB ancora più complicata e fragile.

    
risposta data 23.10.2012 - 20:15
fonte
11

Penso che sarebbe un cattivo progetto non usare il database per le cose per cui è destinato. Non ho mai visto alcun database in cui le regole sono state applicate al di fuori del database che aveva dati buoni. E ho guardato centinaia di database.

Quindi cose che devono essere fatte in un database:

  • Controllo (il controllo solo dell'applicazione non tiene traccia di tutte le modifiche al file database e quindi è senza valore).

  • Vincoli di ingerenza dei dati compresi i valori predefiniti, chiave esterna vincoli e regole che devono essere sempre applicati a tutti i dati. Tutti i dati non vengono sempre modificati o inseriti tramite un'applicazione, lì sono le correzioni di dati una tantum specialmente di grandi serie di dati che sono no pratico per fare un record alla volta (si prega di aggiornare questi 100.000 record che sono stati contrassegnati come status 1 quando dovrebbero essere 2 a causa di un bug del codice dell'applicazione o si prega di aggiornare tutti i record dal client A a cliente B perché la società B ha acquistato la società A) e le importazioni di dati e altre applicazioni che potrebbero toccare lo stesso database.

  • JOINS e where filtering delle clausole (per ridurre il numero di record inviato attraverso la rete)

risposta data 11.11.2012 - 13:54
fonte
5

"Premature optimization is the root of all evil (most of it, anyway) in computer programming" - Donald Knuth

Il database è esattamente questo; il livello dati della tua applicazione. Il suo compito è fornire alla tua applicazione i dati richiesti e memorizzare i dati forniti. La tua applicazione è il posto dove inserire il codice che funziona effettivamente con i dati; visualizzandolo, convalidandolo, ecc.

Sebbene il sentimento nella riga del titolo sia ammirevole e accurato fino a un punto (il nocciolo del filtraggio, della proiezione, del raggruppamento ecc. dovrebbe nel numero schiacciante di casi essere lasciato al DB) , una definizione di "bene" potrebbe essere in ordine. Le attività che SQL Server può eseguire con un alto livello di prestazioni sono molte, ma le attività che è possibile dimostrare che SQL Server esegue correttamente in modo isolato e ripetibile sono pochissime. SQL Management Studio è un IDE di database di grandi dimensioni (soprattutto date le altre opzioni con cui ho lavorato come TOAD), ma ha i suoi limiti, primo fra tutti quello che praticamente lo usi (o qualsiasi codice procedurale che esegui in il DB sottostante) è per definizione un "effetto collaterale" (stato alterato che si trova al di fuori del dominio dello spazio di memoria del processo). Inoltre, il codice procedurale di SQL Server è solo ora, con gli ultimi IDE e strumenti, in grado di essere misurato il modo in cui il codice gestito può utilizzare le metriche di copertura e l'analisi del percorso (in questo modo è possibile dimostrare che questa particolare istruzione if viene rilevata dai test X , Y e Z, e il test X è progettato per rendere vera la condizione ed eseguire quella metà mentre Y e Z eseguono il "else" .Questo, a sua volta, presuppone che tu abbia un test che può impostare il database con un particolare avvio stato, esegui il codice procedurale del database attraverso alcune azioni e asserisci i risultati attesi.

Tutto ciò è molto più difficile e complicato della soluzione fornita dalla maggior parte dei livelli di accesso ai dati; si supponga che il livello dati (e, a questo proposito, il DAL) sappia come eseguire il proprio lavoro quando viene fornito l'input corretto, quindi verificare che il codice fornisca l'input corretto. Mantenendo il codice procedurale come SP e trigger dal DB e facendo invece questi tipi di cose nel codice dell'applicazione, detto codice dell'applicazione è molto più facile da esercitare.

    
risposta data 23.10.2012 - 19:28
fonte
5

Una delle cose che le persone non sembrano rendersi conto è che fare tutta l'elaborazione sul server SQL non è necessariamente buona, indipendentemente dagli effetti sulla qualità del codice.

Ad esempio, se è necessario acquisire alcuni dati e quindi calcolare qualcosa dai dati e quindi archiviare tali dati nel database. Ci sono due scelte:

  • Raccogli i dati nell'applicazione, calcola all'interno dell'applicazione e quindi reinvia i dati al database
  • Creare una stored procedure o simile per acquisire i dati, calcolarli e quindi memorizzarli tutti da un'unica chiamata al server SQL.

Potresti pensare che la seconda soluzione sia sempre la più veloce, ma questo non è assolutamente vero. Sto ignorando anche se SQL è inadatto al problema (es. Regex e manipolazione delle stringhe). Facciamo finta di avere SQL CLR o qualcosa di simile per avere un linguaggio potente anche nel database. Se è necessario 1 secondo per effettuare un round trip e ottenere i dati e 1 secondo per memorizzarlo, e quindi 10 secondi per eseguire il calcolo su di esso. Stai sbagliando se lo stai facendo tutto nel database.

Certo, ti sbarazzi di 2 secondi. Tuttavia, hai preferito sprecare il 100% (almeno) un core della CPU sul server del database per 10 secondi o hai preferito perdere quel tempo sul tuo server web?

I server Web sono facili da scalare, i database dall'altra parte sono estremamente costosi, in particolare i database SQL. Nella maggior parte dei casi, i server Web sono "senza stato" e possono essere aggiunti e rimossi a piacere senza alcuna configurazione aggiuntiva per il bilanciamento del carico.

Quindi, non pensare solo a radere 2 secondi da un'operazione, ma anche a pensare alla scalabilità. Perché sprecare una risorsa costosa come le risorse del server di database quando è possibile utilizzare le risorse del server Web molto più economiche con un impatto relativamente ridotto sulle prestazioni

    
risposta data 26.10.2012 - 19:53
fonte
4

Mi piace guardarlo come SQL dovrebbe trattare solo con i dati stessi. Le regole aziendali che decidono come potrebbe apparire la query possono verificarsi nel codice. La regex o la convalida delle informazioni dovrebbe essere fatta nel codice. SQL dovrebbe essere lasciato solo per unirsi alla tua tabella, interrogare i tuoi dati, inserire dati puliti, ecc.

Ciò che viene passato in SQL dovrebbe essere dati puliti e SQL non dovrebbe realmente sapere nulla di più di quello che ha bisogno di memorizzarlo, aggiornarlo, cancellarlo o recuperare qualcosa. Ho visto come troppi sviluppatori vogliono lanciare la loro logica di business e la codifica in SQL perché pensano ai dati come alla loro attività. Dissocia la tua logica dai tuoi dati e scoprirai che il tuo codice diventa più pulito e più facile da gestire.

Solo $ 0.02.

    
risposta data 23.10.2012 - 18:14
fonte
3

In genere sono d'accordo che il codice dovrebbe controllare la logica di business e il DB dovrebbe essere un hash logico. Ma qui ci sono alcuni contatori:

I vincoli primario, esterno e obbligatorio (non nullo) potrebbero essere applicati dal codice. I vincoli sono la logica aziendale. Dovrebbero essere lasciati fuori dal database poiché duplicano ciò che il codice può fare?

Le altre parti al di fuori del tuo controllo toccano il database? Se così fosse, i vincoli applicati vicino ai dati sono buoni. L'accesso potrebbe essere limitato a un servizio web che implementa la logica, ma questo presuppone che tu fossi lì "prima" e hai il potere di far rispettare l'uso del servizio sulle altre parti.

Il tuo ORM esegue un inserimento / aggiornamento separato per ciascun oggetto? In caso affermativo, si avranno problemi di prestazioni gravi durante l'elaborazione batch di set di dati di grandi dimensioni. Impostare le operazioni è la strada da percorrere. Un ORM avrà problemi nella modellazione accurata di tutte le possibili serie unite su cui è possibile eseguire operazioni.

Considerate un "livello" una divisione fisica da parte dei server o una suddivisione logica? L'esecuzione della logica su qualsiasi server potrebbe teoricamente continuare a ricadere nel suo livello logico. È possibile organizzare la suddivisione compilando esclusivamente server DLL diversi anziché splittati. Ciò può aumentare drasticamente i tempi di risposta (ma sacrificare il rendimento) mantenendo al contempo la separazione delle preoccupazioni. Successivamente, una DLL divisa potrebbe essere spostata su altri server senza una nuova build per aumentare il throughput (al costo del tempo di risposta).

    
risposta data 23.10.2012 - 18:26
fonte
3

L'idioma ha più a che fare con il mantenimento delle regole aziendali, con i dati, con le relazioni (i dati e la struttura e le relazioni). Non è un one-stop-shop per ogni problema, ma aiuta ad evitare cose come i contatori di record mantenuti manualmente, l'integrità della relazione mantenuta manualmente ecc., se queste cose sono disponibili a livello di database. Quindi, se qualcun altro arriva e estende i programmi o scrive un altro programma che interagisce con il database, non dovrà capire come mantenere l'integrità del database dal codice precedente. Il caso di un contatore di record gestito manualmente è particolarmente pertinente quando qualcun altro desidera creare un nuovo programma per interagire con lo stesso database. Anche se il programma appena creato ha esattamente il codice giusto per il contatore, il programma originale e il nuovo in esecuzione all'incirca nello stesso periodo probabilmente lo corromperanno. Esiste anche un codice che recupera i record e controlla le condizioni prima di scrivere un nuovo record o aggiornato (in codice o come query separate), quando, se possibile, questo può essere spesso ottenuto direttamente nell'istruzione di inserimento o aggiornamento. La corruzione dei dati può ancora risultare. Il motore del database garantisce l'atomicità; un aggiornamento o inserimento di query con condizioni è garantito per influenzare solo i record che soddisfano le condizioni e nessuna query esterna può modificare i dati a metà del nostro aggiornamento. Ci sono molte altre circostanze in cui il codice viene utilizzato quando il motore del database dovrebbe servire meglio. Si tratta di integrità dei dati e non di prestazioni.

Quindi in realtà è un buon idioma di progettazione o regola empirica. Nessuna quantità di prestazioni può aiutare in un sistema con dati corrotti.

    
risposta data 24.10.2012 - 01:08
fonte
0

Come accennato in precedenza, l'obiettivo è quello di inviare e ricevere il meno possibile dal database perché i viaggi di andata e ritorno sono molto costosi in termini di tempo. L'invio di statistiche SQL più e più volte è una perdita di tempo, soprattutto nelle query più complesse.

L'utilizzo delle stored procedure nel database consente agli sviluppatori di interagire con il database come un'API, senza preoccuparsi del complesso schema sul retro. Riduce anche i dati inviati al server poiché vengono inviati solo il nome e alcuni parametri. In questo scenario, la maggior parte della logica di bussines può ancora essere nel codice ma non nella forma di SQL. Il codice dovrebbe essenzialmente preparare ciò che deve essere inviato o richiesto dal database.

    
risposta data 24.10.2012 - 02:02
fonte
0

Ci sono alcune cose da ricordare:

  • Un database relazionale dovrebbe garantire l'integrità referenziale tramite chiavi esterne
  • Scalare un database può essere difficile e costoso. Scalare un server web è molto più semplice semplicemente aggiungendo più server web. Divertiti a cercare di aggiungere più potenza al server SQL.
  • Con C # e LINQ, puoi fare i tuoi "join" e tutto il codice in modo da ottenere il meglio da entrambi i mondi in molti casi
risposta data 25.10.2012 - 03:56
fonte
0

"Premature optimization is the root of all evil" - Donald Knuth

Utilizza lo strumento più appropriato per il lavoro. Per l'integrità dei dati, questo è spesso il database. Per le regole aziendali avanzate, questo è un sistema basato su regole come JBoss Drools. Per la visualizzazione dei dati, questo sarebbe un quadro di riferimento. ecc.

Se si verificano problemi di prestazioni, è quindi necessario verificare se i dati possono essere memorizzati nella cache o se un'implementazione nel database sarà più veloce. In generale, il costo di acquisto di server aggiuntivi o di alimentazione cloud aggiuntiva sarà molto inferiore rispetto ai costi di manutenzione aggiuntivi e all'impatto di bug aggiuntivi.

    
risposta data 02.02.2014 - 02:04
fonte

Leggi altre domande sui tag