Le chiamate di database multiple sono davvero significative con una chiamata di rete per un'API Web?


16

In uno dei miei datori di lavoro, abbiamo lavorato su un'API REST (ma vale anche per SOAP). Il client, che è l'interfaccia utente dell'applicazione, effettuerebbe chiamate via Web (LAN in distribuzioni di produzione tipiche) all'API. L'API avrebbe effettuato chiamate al database.

Un tema ricorrente nelle nostre discussioni è rappresentato dalle prestazioni: alcune persone del team ritengono che non si debbano avere più chiamate al database (in genere letture) da una singola chiamata API a causa delle prestazioni; è necessario ottimizzarli in modo che ogni chiamata API abbia solo (esattamente) una chiamata al database.

Ma è davvero importante? Considera che l'interfaccia utente deve effettuare una chiamata di rete all'API; è piuttosto grande (ordine di grandezza di millisecondi). I database sono ottimizzati per mantenere le cose in memoria ed eseguire letture molto, molto rapidamente (ad es. SQL Server carica e mantiene tutto nella RAM e, se possibile, consuma quasi tutta la RAM libera).

TLDR: è davvero importante preoccuparsi di più chiamate al database quando stiamo già effettuando una chiamata di rete sulla LAN? Se è così, perché?

Per essere chiari, sto parlando di ordine di grandezza - so che dipende da specifiche (hardware della macchina, scelta di API e DB, ecc.) Se ho una chiamata che impiega O (millisecondi), ottimizza per DB le chiamate che richiedono un ordine di grandezza inferiore, contano davvero? O c'è di più al problema di questo?

Modifica: per i posteri, penso che sia abbastanza ridicolo affermare che dobbiamo migliorare le prestazioni combinando le chiamate al database in queste circostanze, specialmente con una mancanza di profilazione. Tuttavia, non è una mia decisione se lo facciamo o no; Voglio sapere qual è la logica alla base di questo è un modo corretto di ottimizzare le chiamate API Web.


Non esiste un'altra chiamata di rete tra il livello API e il database?
Firma il

4
Cosa hanno mostrato i tuoi test di cronometraggio?
Dan Pichelman,

@ Firma Non esiste alcuna chiamata di rete tra l'API e il DB. Sono garantiti per essere sulla stessa macchina, da quello che ho capito.
ashes999,

@DanPichelman è quello che sto chiedendo anche a me. Nessuno sembra prendere e cronometrare le prestazioni; otteniamo solo i requisiti per "correggere le prestazioni in X combinando tutte le chiamate DB in una singola chiamata".
ashes999,

Risposte:


25

Ma è davvero importante? Considera che l'interfaccia utente deve effettuare una chiamata di rete all'API; è piuttosto grande (ordine di grandezza di millisecondi). I database sono ottimizzati per mantenere le cose in memoria ed eseguire letture molto, molto rapidamente (ad es. SQL Server carica e mantiene tutto nella RAM e, se possibile, consuma quasi tutta la RAM libera).

La logica

In teoria, hai ragione. Tuttavia, ci sono alcuni difetti con questa logica:

  1. Da quello che hai dichiarato, non è chiaro se hai effettivamente testato / profilato la tua app. In altre parole, si fa realmente sapere che i trasferimenti di rete da l'applicazione per l'API sono la componente più lento? Perché è intuitivo, è facile supporre che lo sia. Tuttavia, quando si discute delle prestazioni, non si deve mai supporre. Al mio datore di lavoro, sono il responsabile delle prestazioni. Quando mi sono unito per la prima volta, le persone continuavano a parlare di CDN, repliche, ecc. In base all'intuizione su quali fossero i colli di bottiglia. A quanto pare, i nostri maggiori problemi di prestazioni erano scarsi risultati delle query del database.

  2. Stai dicendo che, poiché i database sono bravi a recuperare i dati, il database è necessariamente in esecuzione alle massime prestazioni, viene utilizzato in modo ottimale e non è possibile fare nulla per migliorarlo. In altre parole, i database sono progettati per essere veloci, quindi non dovrei mai preoccuparmene. Un'altra linea di pensiero pericolosa. È come dire che un'auto è destinata a muoversi rapidamente, quindi non ho bisogno di cambiare l'olio.

  3. Questo modo di pensare presuppone un singolo processo alla volta o, in altre parole, nessuna concorrenza. Presuppone che una richiesta non possa influenzare le prestazioni di un'altra richiesta. Le risorse sono condivise, come I / O su disco, larghezza di banda di rete, pool di connessioni, memoria, cicli della CPU, ecc. Pertanto, la riduzione dell'utilizzo di una risorsa condivisa da parte di una chiamata al database può impedire che altre richieste rallentino. Quando ho aderito per la prima volta al mio attuale datore di lavoro, il management ha ritenuto che l'ottimizzazione di una query di database di 3 secondi fosse una perdita di tempo. 3 secondi è così poco, perché perdere tempo con esso? Non staremmo meglio con un CDN o una compressione o qualcos'altro? Ma se riesco a far eseguire una query di 3 secondi in 1 secondo, ad esempio aggiungendo un indice, ovvero 2/3 di blocco in meno, 2/3 di tempo in meno dedicato all'occupazione di un thread e, soprattutto, meno dati letti dal disco,

La teoria

C'è una concezione comune secondo cui le prestazioni del software riguardano semplicemente la velocità .

Da una prospettiva puramente veloce, hai ragione. Un sistema è veloce quanto il suo componente più lento. Se hai profilato il tuo codice e scoperto che Internet è il componente più lento, allora tutto il resto non è ovviamente la parte più lenta.

Tuttavia, dato quanto sopra, spero che tu possa vedere come la contesa di risorse, la mancanza di indicizzazione, il codice scritto male, ecc. Possano creare sorprendenti differenze nelle prestazioni.

I presupposti

Un'ultima cosa. Hai detto che una chiamata al database dovrebbe essere economica rispetto a una chiamata di rete dall'app all'API. Ma hai anche detto che l'app e i server API sono nella stessa LAN. Pertanto, non sono entrambi comparabili come chiamate di rete? In altre parole, perché stai supponendo che il trasferimento dell'API sia più lento degli ordini di grandezza rispetto al trasferimento del database quando entrambi hanno la stessa larghezza di banda disponibile? Naturalmente i protocolli e le strutture dei dati sono diversi, lo capisco, ma contesto il presupposto che siano ordini di grandezza diversi.

Dove diventa murkey

L'intera domanda riguarda le chiamate di database "multiple" contro "single". Ma non è chiaro quanti siano multipli. A causa di ciò che ho detto sopra, come regola generale, consiglio di effettuare il numero di chiamate al database necessario. Ma questa è solo una regola empirica.

Ecco perché:

  1. I database sono bravi a leggere i dati. Sono motori di archiviazione. Tuttavia, la logica aziendale risiede nell'applicazione. Se si stabilisce una regola secondo cui ogni chiamata API genera esattamente una chiamata al database, la logica aziendale potrebbe finire nel database. Forse va bene. Molti sistemi lo fanno. Ma alcuni non lo fanno. Si tratta di flessibilità.
  2. A volte per ottenere un buon disaccoppiamento, è necessario separare 2 chiamate al database. Ad esempio, forse ogni richiesta HTTP viene instradata attraverso un filtro di sicurezza generico che convalida dal DB che l'utente ha i diritti di accesso corretti. In tal caso, procedere con l'esecuzione della funzione appropriata per tale URL. Tale funzione potrebbe interagire con il database.
  3. Chiamata al database in un ciclo. Questo è il motivo per cui ho chiesto quanti sono multipli. Nell'esempio sopra, avresti 2 chiamate al database. 2 va bene. 3 potrebbe andare bene. N non va bene. Se si chiama il database in un ciclo, le prestazioni sono ora lineari, il che significa che richiederà più tempo rispetto all'input del ciclo. Quindi dire categoricamente che il tempo di rete dell'API è il più lento trascura completamente le anomalie come l'1% del traffico impiegando molto tempo a causa di un ciclo non ancora scoperto che chiama il database 10.000 volte.
  4. A volte ci sono cose in cui la tua app è migliore, come alcuni calcoli complessi. Potrebbe essere necessario leggere alcuni dati dal database, eseguire alcuni calcoli, quindi in base ai risultati, passare un parametro a una seconda chiamata al database (magari per scrivere alcuni risultati). Se li combini in una singola chiamata (come una procedura memorizzata) solo per poter chiamare il database solo una volta, ti sei costretto a utilizzare il database per qualcosa in cui il server delle app potrebbe essere migliore.
  5. Bilanciamento del carico: hai 1 database (presumibilmente) e più server di applicazioni con bilanciamento del carico. Pertanto, maggiore è il lavoro svolto dall'app e minore è il database, più facile è ridimensionare perché è generalmente più facile aggiungere un server app rispetto alla configurazione della replica del database. Sulla base del precedente punto elenco, potrebbe essere logico eseguire una query SQL, quindi eseguire tutti i calcoli nell'applicazione, che viene distribuita su più server, quindi scrivere i risultati al termine. Ciò potrebbe fornire un throughput migliore (anche se il tempo complessivo della transazione è lo stesso).

TL; DR

TLDR: è davvero importante preoccuparsi di più chiamate al database quando stiamo già effettuando una chiamata di rete sulla LAN? Se è così, perché?

Sì, ma solo fino a un certo punto. Dovresti cercare di ridurre al minimo il numero di chiamate al database quando possibile, ma non combinare le chiamate che non hanno nulla a che fare l'una con l'altra solo per il gusto di combinarle. Inoltre, evitare di chiamare il database in un ciclo a tutti i costi.


3

Sembra che la tua squadra stia ottimizzando prima che abbiano motivo di farlo. Hai misurato il tempo per eseguire queste richieste? È probabile che questo paradigma crei prestazioni peggiori per l'utente finale poiché i viaggi di andata e ritorno al server Web avranno una latenza molto più elevata rispetto al tempo di connessione dal server Web al database. Inoltre, la maggior parte dei browser Web effettuerà solo 2 connessioni simultanee a un singolo server Web, quindi per le pagine complesse probabilmente ci si imbatterà in un collo di bottiglia.

In ogni caso, le decisioni di ottimizzazione non dovrebbero essere prese senza dati per il backup. Misuralo e scopri cosa è meglio per la tua applicazione.


1
Questo è un buon commento sulle nostre scarse prestazioni prestazionali, ma non risponde alla mia domanda se le chiamate DB sono qualcosa di cui preoccuparsi quando ho già una chiamata di rete.
ashes999,

1
In generale, ho riscontrato che effettuare più chiamate al database non è un problema. Ciò è dovuto principalmente al pool di connessioni e alla piccola latenza tra il DB e il server Web. C'è un punto in cui effettuare un sacco di chiamate db diverse avrà un impatto negativo sulle prestazioni, ma non ho un numero difficile per te. Dipende tutto dall'ambiente e dall'applicazione. Solo la misurazione ti darà la risposta che cerchi.
brianfeucht,

Non dovrebbe (necessariamente) dipendere da specifici, perché sto parlando di ordine di grandezza.
ashes999,

Solo ipotesi approssimative (è necessario misurare): Tempo medio di connessione al DB dal server Web: 2 ms Tempo medio di connessione al server Web dal client: 20 ms Quindi, supponendo che quei numeri che ho estratto casualmente dall'aria siano corretti, potresti fare 10 le chiamate al database nel tempo necessario per effettuare una chiamata al servizio web. Supponendo che le query del database richiedano la stessa quantità di tempo. Quei numeri dipendono estremamente dall'ambiente. Se il client che effettua la chiamata al servizio Web è locale, potrebbe ridurlo di diversi ordini di grandezza.
brianfeucht,

2

Non possiamo dirtelo.

Non abbiamo l'aspetto delle tue domande. Non sappiamo quanto tempo impiegano per completare. Non sappiamo quanto costi generali siano coinvolti in ogni richiesta al tuo server API. Non sappiamo quanto siano geograficamente dispersi i tuoi clienti. Eccetera.

Se questo è uno scenario che richiede l'ottimizzazione ed è uno in cui puoi decidere se dividere o unire le chiamate insieme, devi confrontarlo in entrambi i modi : Decidi cosa stai ottimizzando (latenza dell'interfaccia utente, carico della CPU del server, contesa, ecc.) e scegli quello che meglio raggiunge il tuo obiettivo di ottimizzazione.


A parte questo, l'unica una cosa che posso aggiungere con relativa certezza è questa:

All'interno di una singola richiesta, è necessario eseguire tutte le query necessarie per creare una risposta.

In altre parole, se la risposta non può essere generata fino a quando non vengono eseguite tutte le query N, di solito è insensato separarle. Se riesci a generare risultati significativi, intermedi o completi, dopo ogni query, avvia il benchmarking.


1

Due pensieri:

Innanzitutto, per il consumatore che utilizza l'API, sta effettuando una chiamata per eseguire un'attività. Ciò che accade dopo che il server ha ricevuto la chiamata per riempire la richiesta non dovrebbe essere così rigido. Se quella chiamata da un consumatore richiede 10 articoli di lavoro secondario per riunire i dati e restituirli, dovrebbe essere accettabile.

Secondo: vedi un reale problema di prestazioni del database con il processo in questione? La mia esperienza ha dimostrato che spesso il tentativo di inserire tutti gli aspetti di una richiesta di database in una singola chiamata può comportare una chiamata meno efficiente rispetto alla semplice esecuzione di tre o quattro chiamate per i dati. I database moderni sono molto efficienti nella memorizzazione nella cache e nei piani di esecuzione. Spesso, quando provi a fare troppo, vedrai le procedure con i cursori (molto male per le prestazioni perché i dati vengono agiti riga per riga, non come un insieme in una volta) e codice che si traduce in un piano meno efficiente rispetto a se avessi rotto la chiamata in pochi semplici passaggi.

Per semplice organizzazione del codice, sono d'accordo sul fatto che ogni chiamata API dovrebbe eventualmente chiamare una singola procedura memorizzata (o funzione db) che a sua volta è responsabile del riempimento della richiesta. Potrebbe esserci più di un passaggio nella procedura.


Concordo con te sulla misurazione delle prestazioni, cosa che nessuno sembra fare. Non ci sono prove che questo sia più veloce, ma continua a emergere. Le prestazioni si presentano come un problema quando abbiamo alcune chiamate che possono fare, diciamo, 1000 DB SELECT.
ashes999,

@ ashes999 mentre potresti aumentare la velocità osservando il numero di chiamate db, è più probabile che si trovi nella strategia di indicizzazione ecc. non nel numero di chiamate. Come tutti hanno indicato, guarda i dati sulle prestazioni.
Richard,

Richard, sono d'accordo e lo so davvero. La mia domanda è perché varie persone continuano a sollevare questo punto secondo cui "più chiamate DB sono lente" quando è coinvolta una chiamata di rete. Non vedo davvero come possa essere significativo.
ashes999,

@ ashes999 Mi dispiace, forse dovresti entrare in qualche dettaglio in più sulla chiamata di rete, dato che sembra ovvio, ho l'impressione che ci sia qualcosa in più nella tua domanda. Sento che ci manca qualcosa nelle tue domande. Subirai sempre un po 'di latenza di rete e ogni chiamata potenzialmente aumenta di "x" volte per ogni chiamata (in termini semplici). L'affermazione al valore nominale è vera, più chiamate di rete saranno più lente di una chiamata di rete al db. Ecco perché suggerisco una chiamata a una procedura memorizzata, quindi, che può effettuare più chiamate al db senza le chiamate multi rete.
Richard,

1

Se il database si trova su un server diverso rispetto al servizio REST, ogni chiamata al database comporterà un roundtrip di rete e ciò potrebbe compromettere significativamente le prestazioni:

Una volta ho osservato che una singola chiamata al servizio web veniva tradotta in circa 500 query del database - questo non era certo un problema quando sia il servizio web che il database si trovano sulla stessa macchina, ma si sono trasformati in un tempo di risposta di 6-7 secondi quando erano su diversi macchinari.

Ovviamente, 500 viaggi di andata e ritorno nel database sono piuttosto estremi. Non sono sicuro di quali siano i tuoi requisiti di prestazione, ma come regola empirica direi che se rimani sotto circa 10 query di database per chiamata REST non dovresti riscontrare un significativo aumento delle prestazioni.


1

Abbiamo un paio di applicazioni molto, molto loquaci. C'è una chiamata al database per ogni. Singolo. Poco. Cosa. Fornire dati di riferimento ancora e ancora e ancora è una parte importante del carico di lavoro sul sistema. Tutta la pianificazione dei thread di lavoro, l'acquisizione e il rilascio di blocchi, la pianificazione del controllo cache ecc. Si sommano anche se non esiste un IO del disco reale. La contesa è maggiore perché le transazioni devono contenere blocchi tra più chiamate DB e quindi la velocità effettiva è molto più bassa di quanto potrebbe essere. Quei team stanno ora cercando di acquistare nuovi server DB molto costosi per questo motivo.

Pertanto, sebbene la maggior parte del tempo trascorso nella configurazione corrente del sistema sia impiegata con le chiamate API REST, ignorare le prestazioni a livello di DB comporta la memorizzazione di problemi per il futuro.


0

Il percorso di ottimizzazione presentato è semplicemente il modo sbagliato di guardare le cose.

Le chiamate API dovrebbero essere atomiche. In altre parole, dovrei essere in grado di effettuare 1 chiamata all'API Web per eseguire l'azione desiderata. Che si tratti di recuperare dati, aggiornare un record o altro. Non dovrebbe MAI richiedere più di 1 chiamata per provocare l'azione. E il tentativo di sfruttare le transazioni tra più chiamate dovrebbe essere evitato come la peste.

A volte una singola azione è piuttosto complessa. Ad esempio, recuperare i dati che vengono combinati da diverse fonti: di nuovo, questa dovrebbe essere una singola chiamata. O l'intera cosa funziona o l'intera cosa fallisce.

Ora, dire che una singola chiamata API dovrebbe eseguire solo una query DB è un po 'idiota. Come hai sottolineato, le spese generali per il marshalling della chiamata attraverso la rete sono spesso ordini di grandezza più costosi in termini di tempo complessivo.

Riesco a capire in qualche modo la loro affermazione che una singola query può essere eseguita più velocemente di molte; ma questo dà una falsa impressione poiché ignora il carico totale di DB e rete. Solo profilando i vari modi di estrarre i dati dal DB puoi capire qual è il problema. Sono sicuro che ognuno ha una storia in cui una particolare query eseguita 100 volte più spesso del previsto ha ucciso il sistema fino a quando non è stato creato un indice adeguato ...

Alla fine non sarai in grado di convincerli solo con le chiacchiere. Impostare un caso di test per entrambi gli approcci e profilarli. Presta attenzione al tempo totale necessario per acquisire i dati di cui hai bisogno, la quantità di traffico di rete generata, il numero e la tempistica delle chiamate al database, ecc. Adotta un approccio olistico, ovvero guarda l'intero sistema e dovresti finire con un sacco di dati per mangiare il corvo o mostrare loro il percorso d'oro.

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.