Quando caricare i dati del database correlati nella richiesta web?

4

Sto provando a determinare quando un'applicazione web deve interrogare un database per i dati correlati che possono o non possono essere utilizzati nella richiesta corrente.

Ad esempio, considera un database che tiene traccia delle risorse per un'azienda. C'è una tabella delle risorse, una tabella dei proprietari e una tabella degli utenti. Le risorse possono essere assegnate a un OwnerID e i record di asset tracceranno gli UserID che hanno creato, aggiornato e cancellato l'asset.

Quando faccio richieste web per i dati, vedo due opzioni principali:

  1. Leggi tutti i dati in una richiesta, con un join in Proprietari e più join in Utenti (utente creato / aggiornato / cancellato). Mentre questo rende più difficile una query, ridurrebbe le chiamate ripetute al database. Semplifica inoltre l'uso di OOP all'interno dell'applicazione, poiché ogni entità sarà completamente popolata e facilmente accessibile, ad esempio: Asset.Owner.Email, Asset.LastUpdatedBy.Name, ecc.

  2. legge solo i dati dalla tabella dei proprietari in primo piano e crea richieste di database separate quando l'applicazione richiede informazioni di proprietario o utente diverse dall'ID. In circa la metà delle richieste HTTP, non saranno richieste informazioni aggiuntive, e nell'altra metà delle richieste HTTP, l'applicazione dovrà interrogare il nome, l'email, ecc. Del proprietario e di ciascuno degli utenti che hanno creato, aggiornato o eliminato la risorsa. Mi piace l'idea della richiesta iniziale snella, ma mi preoccupo delle prestazioni quando faccio altre richieste per ottenere i dati correlati. Inoltre, complica il design OOP nell'applicazione; Invece di essere in grado di recuperare semplicemente Asset.Owner.Email, dovrò prima controllare se l'oggetto Asset.Owner è stato popolato, chiamare Asset.LoadOwnerByID (Asset.OwnerID), quindi accedere finalmente al valore. (Ignora il fatto che Asset non dovrebbe avere un metodo LoadOwnerByID - questo è solo un esempio.)

Ora che l'ho scritto, mi sto orientando verso l'Opzione 1 come il modo più efficiente e semplice di fare le cose. Ma diventa un po 'meno ovvio quando si aggiungono più relazioni ricorsive ai dati, ad esempio: ogni utente ha un valore ReportsToUserID che punta a un altro utente e ogni proprietario ha un campo AccountManagedByUserID che punta a un utente.

In questi esempi, qual è il tuo modo preferito di fare le cose e che cosa hai trovato per essere il più efficiente, sia in termini di prestazioni che di complessità della programmazione?

PS - Sto intenzionalmente tralasciando le specifiche sul framework web e sul tipo di database perché ho avuto questa domanda in tutti gli ambienti, da ASP.Net / MSSQL a Java / SQLite a PHP / MySQL, e credo che i principi si applicherà generalmente a tutti questi ambienti.

    
posta Benjamin Ray 27.08.2015 - 14:38
fonte

3 risposte

1

Nel tuo livello DAO, dovresti avere un modo per contrassegnare una relazione come richiesta o facoltativa (i termini possono variare in base a qualsiasi framework tu usi).

Le relazioni obbligatorie significano che se A link a B e I query A, il servizio web deve restituire anche B. Usalo per i dati in cui i due oggetti sono sempre usati insieme.

Le relazioni facoltative significano che se A collega a B e io interrogano A, il servizio web non restituirà anche B. Se il consumatore del servizio vuole B, invierà un'altra query.

Questo dovrebbe trovare un buon equilibrio. I consumatori che hanno bisogno di pochi dati ottengono pochi dati in una singola query. I consumatori che hanno bisogno di più dati devono ancora eseguire meno query a causa di alcune query che restituiscono più record.

Questo aiuta davvero perché una volta che si hanno relazioni ricorsive, i grafi degli oggetti possono diventare di dimensioni oscenamente grandi anche se si rileva la ricorsione e si usa il valore esistente (ad esempio A - > B - > A solo interrogazioni A e B e imposta i loro collegamenti tra loro piuttosto che la richiesta A). Ho visto che i servizi restituiscono più di 50 oggetti per una singola richiesta e la maggior parte di essi non era necessaria.

Punti bonus se si inserisce il rilevamento di riferimento morto. Un collegamento a B, ma il servizio non ha restituito B. Cioè, A ha l'ID della chiave esterna di B, ma nessun oggetto B restituito. Quando ottieni B da A, qualcosa a livello di framework rileva se è assente e tira B dal servizio web dietro le quinte, rendendo il codice client completamente inconsapevole dei due tipi di relazioni.

    
risposta data 28.08.2015 - 05:07
fonte
1

I'm trying to determine when a web application should query a database for related data that may or may not be used in the current request.

Per domande di questo tipo è difficile dare una risposta generale.

Devi considerare due punti: convenienza e prestazioni .

Molte cose, che a prima vista sembrano convenienti, sono a posteriori con i performancekillers. Che sia l'uno o l'altro - o forse nessuno dei due, dipende dalla quantità di dati nel DB, che è riconducibile al ciclo di vita di un'applicazione.

Se inizi con un'applicazione, spesso non hai più dati. Quindi, per ridurre al minimo i roundtrip al DB, raccogli più dati possibili con una sola richiesta. Vuoi presentare rapidamente i dati avanzati .

Con insiemi di dati di dimensioni ridotte il set di risultati è ridotto, le query, anche se complesse, sono abbastanza veloci, le join sembrano economiche.

Questo cambia col tempo. Le tabelle conterranno molti e molti dati. Le grandi domande diventeranno costose e dovrai interrompere i tuoi concetti. A volte anche sarebbe meglio evitare i join del database e farlo nella memoria principale.

Dal mio punto di vista la domanda - come richiesto - ha una natura filosofica piuttosto .

Tuttavia, ci sono alcune regole pratiche che si possono dare:

1) recupera sempre meno dati eventualmente necessari. Questa è una specie di YAGNI (non ne avrai bisogno). Anche se sembra bello avere i dati a portata di mano: se tu (l'utente) non ne hai bisogno, è inutile cercarlo

2) misura le tue query. Nonostante il punto precedente: se la tua applicazione è abbastanza veloce , perché preoccuparsi? Esistono diversi punti di misura:

  • UX - a volte solo psicologico: pensi o percepisce l'app come veloce
  • Quanto è veloce la rete tra l'utente e il datacenter?
  • Il tuo database offre dati molto velocemente? Le query miglior sforzo ?

Finché i tuoi utenti si sentono scattanti , la connessione è piacevole e le tue query sono veloci, non c'è alcun problema a interrogare quanti più dati possibile.

A seconda di queste metriche rudimentali potresti valutare le tue due soluzioni:

Se hai set di dati di piccole dimensioni , un DB veloce , una connessione da buona a media , puoi interrogare quanti più dati puoi ottenere , che risulterebbe in un'app super veloce. D'altro canto. Se questo è il caso, non ci sarebbe alcuna differenza evidente alla seconda opzione, per interrogare i dati quando necessario.

Ma fai attenzione alle modifiche:

  • se i set di dati crescono grandi e la connessione rimane buona , l'opzione 2 diventa più attraente.

  • ma improvvisamente il tuo utente è sul cellulare - che è spesso un'esperienza orribile - una query costosa potrebbe essere più attraente, anche se presenta tutti degli svantaggi: l'utente potrebbe non fidarsi di una connessione stabile - e tu sei in grado di fornire una bella UX con tutti i dati ricevuti.

tl; dr

When to load related database data in web request?

Dipende.

    
risposta data 28.08.2015 - 09:54
fonte
0

Dovresti avere richieste semantiche molto specifiche.

Ad esempio:

Vuoi estrarre informazioni su un proprietario specifico

example.com/owner/123

Vuoi estrarre informazioni sulle risorse di un proprietario specifico

example.com/owner/123/assets

Ora vuoi estrarre tutte le risorse che appartengono al proprietario 123

example.com/ownerAndAssets/123

O vuoi richiamare un utente specifico:

example.com/user/345

Vuoi estrarre tutte le azioni dell'utente:

example.com/user/345/actions

Vuoi estrarre le 10 azioni utente più recenti per un dato utente:

example.com/user/345/actions/limit/10

Vuoi ritirare un utente e le sue 10 azioni più recenti:

example.com/userAndActions/345/limit/10

Se si architettano bene le query del database nei repository, dovrebbe essere semplice contenere tutte le funzionalità necessarie nelle classi semplici.

    
risposta data 28.08.2015 - 21:16
fonte