Cosa qualifica "troppe richieste di database" nel codice?


17

Questa è una discussione io stesso e alcuni dei miei colleghi stanno avendo e pensato che sarei venuto qui e vedere cosa succede se c'è un consenso generale su di esso.

Fondamentalmente si riduce alle seguenti 2 opinioni sulle chiamate al database: 1. Effettuare una grande chiamata per ottenere tutto ciò che potrebbe essere necessario per ridurre il numero di chiamate nel database del database 2. Effettuare chiamate separate più piccole in base a ciò che viene richiesto per ridurre le dimensioni di Chiamate DB

Dove questo è particolarmente in gioco è in codice comune. Useremo l'esempio di una classe Employee in quanto abbastanza semplice.

Supponiamo che la tua classe Employee abbia 10 attributi di valore (nome, cognome, hiredate, ecc.) E quindi 2 attributi di classe ... 1 che punta a una classe Department e quindi 1 supervisore che punta a un altro oggetto Employee.

Nella mentalità n. 1, effettueresti una chiamata che restituisce i dati del Dipendente nonché i campi necessari per popolare gli attributi Dipartimento e Supervisore ... o almeno i campi che più spesso vengono utilizzati da questi oggetti secondari.

Nella mentalità n. 2, inizialmente si popolerebbe solo l'oggetto Employee e poi si popolerebbero solo gli oggetti Department e Supervisor se e quando effettivamente richiesti.

La posizione di 2 è piuttosto semplice ... minimizza la dimensione delle richieste e quanti oggetti del database devono essere colpiti ogni volta che viene fatta una di quelle richieste. La posizione n. 1 è che anche se potesse essere implementato correttamente, il solo fatto che il codice debba effettuare connessioni multiple provocherà una maggiore tensione sulla connessione tra il server web e il database invece di ridurlo.

La forza trainante dietro la ricerca di questo è che la quantità di traffico tra il nostro server web e il server database sta andando fuori controllo.


7
Nella mia esperienza non esiste una "risposta giusta" a questo. C'è un equilibrio tra latenza e velocità effettiva. Una bassa latenza può tollerare molte piccole richieste o anche una grande; tuttavia, i collegamenti ad alta latenza tendono ad essere migliori spostando molti dati contemporaneamente. Tuttavia, se il throughput è basso in una configurazione ad alta latenza, è meglio recuperare blocchi più piccoli per essere più reattivo.

3
Probabilmente correlato al problema n + 1 stackoverflow.com/questions/97197/…
Valera Kolupaev,

@Valera: per comodità ecco il link pubblicato su quella domanda: realsolve.co.uk/site/tech/hib-tip-pitfall.php?name=n1selects
rwong

4
"la quantità di traffico tra il nostro server web e il server database sta andando fuori controllo." Cosa significa? Puoi essere specifico su quale sia il vero problema? Hai problemi di prestazioni? Hai fatto la profilazione e la misurazione? Fornire i risultati effettivi dalle misurazioni effettive come parte della domanda. Altrimenti, stiamo solo indovinando.
S.Lott

Risposte:


8

Se la forza trainante dietro questa domanda è troppo traffico, hai mai cercato nella cache oggetti usati di frequente? Ad esempio: dopo aver ottenuto gli oggetti Employee e Department e Supervisor, forse sarebbe una buona idea aggiungerli una cache in modo che se vengono richiesti di nuovo nel prossimo futuro, sono già nella cache e non devono essere recuperati ancora. Ovviamente, la cache dovrà far scadere gli oggetti usati raramente e dovrà anche essere in grado di rimuovere oggetti che sono stati modificati dall'applicazione e salvati nel database.

A seconda della lingua e dei framework che stai utilizzando, potrebbe esserci già un framework di cache che può fare parte (o la maggior parte) di ciò di cui hai bisogno. Se si utilizza Java, è possibile esaminare Apache Commons-Cache (non l'ho usato per un po ', e mentre sembra dormiente, è ancora disponibile per l'uso ed è stato abbastanza decente l'ultima volta che l'ho usato).


3

Cerca sempre leggibilità e chiarezza la prima volta che scrivi qualcosa. È quindi possibile refactoring se e quando è necessario. Esegui test di carico per trovare i colli di bottiglia, in molti casi non è il numero di chiamate che causano il problema, ma quelle scritte male.

Quanto a ciò che classifica come troppi, dipende dall'applicazione. Per la maggior parte delle applicazioni Web è accettabile qualcosa di meno di 30 secondi. Vorrei parlare ai tuoi utenti delle loro aspettative.


Cosa costituisce una chiamata db scritta male?
Nu Everest,

3

La tua domanda sembra basata sul presupposto che devi indovinare quali dati saranno necessari per una determinata pagina. Non è così. Non è facile come l'approccio ingenuo, ma puoi progettare il tuo codice in modo da sapere se avrai bisogno degli attributi di reparto o supervisore prima di effettuare qualsiasi chiamata al database.


3

Queste sono le regole che uso, forse ti saranno utili.

  1. Misura prima! Non guarderò nemmeno il codice che "potrebbe essere lento" a meno che non riesca effettivamente a vedere il traffico che scorre a quella risorsa e che la risorsa sta rispondendo lentamente.
  2. 1 richiesta = K query. Il numero di volte in cui parlo con il database è completamente determinato dal tipo di risorsa richiesta; e mai per la natura della richiesta o dello stato di quella risorsa; Nel tuo esempio, sono probabilmente al massimo 3 query: 1 per dipendenti, 1 per dipartimenti e 1 per supervisori; Non importa quanti ce ne siano di ciascuno.
  3. Non interrogare ciò che non utilizzerai . Se si tratta di HTTP di cui stiamo parlando, non ha senso interrogare i dati per dopo; non c'è più tardi; ogni richiesta inizia da una lavagna pulita. A volte ne ho più bisogno delle colonne da una tabella, ma a volte ne ho bisogno solo una o due; quando conosco esattamente i campi di cui ho bisogno, chiederò proprio quello.
  4. Lancia l'hardware al problema. I server sono economici; A volte è possibile ottenere prestazioni sufficienti semplicemente spostando il database in una scatola più robusta; o invio di alcune query a una replica di sola lettura.
  5. Prima invalidare la cache, quindi implementare la memorizzazione nella cache. L'impulso di mettere i dati spesso usati o difficili da interrogare in una cache è forte; ma troppo spesso, lo sfratto dei dati non utilizzati o la scadenza dei dati sostituiti viene trascurato. Se sai come estrarre i dati dalla cache; allora sei sicuro di metterlo nella cache; Se risulta più costoso invalidare la cache che semplicemente fare la query; quindi non hai bisogno di una cache.

2

Entrambe le strategie qui sono perfettamente valide. Ci sono vantaggi e svantaggi per ciascuno:

Una chiamata per tutti e 3 gli oggetti:

  • si esibirà più velocemente
  • ti fornirà esattamente ciò di cui hai bisogno nel caso in cui ne hai bisogno
  • sarà probabilmente utilizzabile solo in un caso (potrebbe essere un caso molto comune)
  • sarà più difficile da mantenere
  • dovrà essere mantenuto più spesso (poiché cambierà se uno qualsiasi degli schemi dei 3 oggetti o i dati necessari cambiano)

Una chiamata per oggetto (3 chiamate in totale)

  • Ti dà una chiamata di uso generale per popolare una singola istanza di ciascun tipo di oggetto; possono quindi essere utilizzati indipendentemente
  • Sarà più gestibile in quanto la struttura della query sarà più semplice.
  • Sarà più lento (non necessariamente 3 volte più lento, ma l'overhead è aumentato per gli stessi dati)
  • Può causare problemi con il recupero di dati non necessari (è inutile estrarre l'intero record quando è necessario un campo)
  • Può causare problemi N + 1 quando esiste una relazione molti-a-uno, se la query a record singolo viene inviata N volte, una per record nella raccolta.

In risposta a un paio di dubbi (n. 3 e 5 nella seconda lista) ... Cosa fare se supervisore e dipartimento vengono utilizzati solo 1/3 (o meno) del tempo? Cosa succede se il codice è stato progettato per ottenere tutti i bambini non appena viene fatto riferimento all'oggetto List <> codificato per contenerli? ... faciliterebbe la maggior parte della diffidenza?
user107775

Se gli oggetti ausiliari sono necessari solo raramente, nel caso generale ciò funzionerà più velocemente (meno dati da recuperare) ma il caso peggiore sarà più lento (stessi dati o più recuperati, utilizzando tre volte l'overhead di comunicazione dal computer). Per quanto riguarda il problema N + 1, devi semplicemente essere in grado di progettare la query che recupera un elenco di oggetti per poterlo fare in base alla chiave esterna sul lato "uno" della relazione, quindi estrarre più righe dal risultato della query. Non è possibile utilizzare una versione della query che deve avere la chiave primaria del record.
KeithS,

1

Per me, troppe richieste DB stanno facendo più richieste di quelle necessarie per caricare i dati richiesti in qualsiasi momento.

Quindi io non hai bisogno dei dati, non sprecare memoria per ottenerli per evitare un secondo viaggio in seguito. Ma se hai bisogno della quantità di dati, dovresti ridurre al minimo le chiamate al db.

Quindi, hai entrambe le opzioni e usa ognuna dove la situazione lo richiede.

EDIT: tieni presente che questo corso dipende anche dalla tua situazione. Se si tratta di un'app Web, ad esempio, è necessario tenere conto di considerazioni diverse rispetto a se si tratta di un'app desktop che accede al DB all'interno della rete, anziché attraverso il Web per WepApp.


Che dire nel caso in cui stai scrivendo un codice comune e non sei sicuro del modo in cui verrà utilizzato il tuo codice. Forse non immagineresti mai che qualcuno non abbia bisogno del supervisore, ma si scopre che l'applicazione su cui lavori è l'unica che ne ha bisogno. Certo, potresti scrivere funzioni separate ... una per non includerla e un'altra per includerla ma a che punto il tuo codice comune inizia a richiedere troppe conoscenze dettagliate per poterlo usare?
user107775

@ user107775 Di solito scrivo solo due funzioni per ogni caso; uno che restituisce solo i valori delle proprietà e uno che restituisce la classe con tutte le classi correlate. Questo perché la maggior parte delle volte, hai solo bisogno delle proprietà. In questo modo, non hai bisogno di conoscenza dei dettagli, solo l'uno ottiene le basi e l'altro tutto. Trovo che sia un equilibrio ragionevole. (Tuttavia alcuni casi specifici richiedono una maggiore ottimizzazione, ma questo caso per caso).
AJC,

1

Connettersi al DB, inviare la richiesta e averla analizzata di solito richiede molto tempo rispetto al recupero dei risultati, quindi la tendenza generale è concatenare quante più query possibili in una richiesta.

Tuttavia, farlo tutto in una volta renderà il codice non mantenibile. Invece, di solito viene raggiunto da un ulteriore livello di astrazione: il codice pianifica diverse richieste quando sono necessarie, quindi il motore analizza questa come un'unica grande richiesta (possibilmente utilizzando la cache sulla strada) e quindi le risposte vengono inviate quando necessario.

Naturalmente non sempre tutti possono essere recuperati in una query: spesso avrai una query che fornisce i dati necessari per creare la query successiva, quindi dovrai ripeterla. Tuttavia, scaglionare fasci di query ed eseguire il maggior numero possibile contemporaneamente è meglio di centinaia di piccoli colpi al database.

Quindi, pianifica ciò di cui hai bisogno, richiedilo e recuperalo, se è necessario altro, richiedilo e recuperalo di nuovo, quindi utilizza i dati per generare contenuto. Evita assolutamente di utilizzare richieste di database come l'inizializzazione di variabili locali sparse in tutto il codice.


1

Non sappiamo abbastanza della tua applicazione per sapere quale scelta sei colpevole di ottimizzare troppo presto. Con quale frequenza vengono utilizzati i dati del supervisore? Sembra che potrebbe essere uno spreco, ma non lo sappiamo. Se li tieni separati, potresti essere in grado di monitorare il tuo sistema per vedere con quale frequenza finiscono per essere utilizzati insieme. Quindi puoi decidere di combinarli in una sola chiamata. Altrimenti, se inizi a creare un collo di bottiglia con questa grande chiamata, da dove inizi a risolvere i problemi? Difficile identificare ciò che ha senso omettere. Altri campi dati potrebbero essere aggiunti a questo processo.

Sarebbe interessante sapere quanto proviene dalla memoria del db rispetto al disco. Non c'è nulla che mi faccia pensare che il dipartimento abbia più o meno probabilità di cambiare rispetto all'indirizzo.

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.