25

Stiamo progettando un'app per Android che ha molti dati ("clienti", "prodotti", "ordini" ...) e noi doniamo 'Voglio interrogare SQLite ogni volta che abbiamo bisogno di un record. Vogliamo evitare di interrogare il database il più possibile, quindi abbiamo deciso di mantenere certi dati sempre in memoria.Best practice per mantenere i dati in memoria e database contemporaneamente su Android

La nostra idea iniziale è quella di creare due classi semplici:

  1. "MemoryRecord": una classe che conterrà essenzialmente un array di oggetti (string, int, double, datetime, ecc ...), che sono i dati di un record di tabella e tutti i metodi per ottenere quei dati in/out da questo array.

  2. "MemoryTable": una classe che conterrà essenzialmente una mappa di [Key, MemoryRecord] e tutti i metodi per manipolare questa mappa e inserire/aggiornare/eliminare record in/dal database.

Tali classi saranno derivate da ogni tipo di tabella presente nel database. Naturalmente ci sono altri metodi utili non elencati sopra, ma a questo punto non sono importanti.

Quindi, quando avvieremo l'applicazione, caricheremo tali tabelle da un database SQLite alla memoria usando quelle classi, e ogni volta che dovremo modificare alcuni dati, cambieremo in memoria e li inseriremo nel database subito dopo.

Ma, vogliamo qualche aiuto/consiglio da parte vostra. Puoi suggerire qualcosa di più semplice o efficace per implementare una cosa del genere? O forse alcune classi esistenti che già lo fanno per noi?

Capisco quello che voi ragazzi state cercando di mostrarmi, e vi ringrazio per questo.

Ma supponiamo di avere una tabella con 2000 record e sarà necessario elencarli. Per ognuno, devo interrogare altre 30 tabelle (alcune con 1000 record, altre con 10 record) per aggiungere ulteriori informazioni nella lista, e questo mentre è "volante" (e come sapete, dobbiamo essere molto veloci in questo momento).

Ora dirai: "costruisci la tua query principale con tutti questi" join "e porta tutto ciò che ti serve in un unico passaggio: SQLite può essere molto veloce, se il tuo database è ben progettato, ecc. .. ".

OK, ma questa query diventerà molto complicata e sicura, anche se SQLite è molto veloce, sarà "troppo" lento (2 a 4 secondi, come ho confermato, e questo non è un tempo accettabile per noi).

Un altro complicatore è che, in base all'interazione dell'utente, è necessario "interrogare nuovamente" tutti i record, perché le tabelle coinvolte non sono le stesse e dobbiamo "re-join" con un altro insieme di tabelle.

Quindi, un'alternativa è portare solo i record principali (questo non cambierà mai, non importa cosa l'utente fa o vuole) senza alcun join (questo è molto veloce!) E interrogare le altre tabelle ogni volta che vogliamo alcuni dati. Nota che sul tavolo con solo 10 record, recupereremo gli stessi record molte e molte volte. In questo caso, è uno spreco di tempo, perché non importa quale sia lo SQLite veloce, sarà sempre più costoso interrogare, spostare il cursore, recuperare, ecc. Piuttosto che prendere il record da una sorta di "cache di memoria". Voglio chiarire che non abbiamo in programma di mantenere sempre tutti i dati in memoria, solo alcune tabelle vengono interrogate molto spesso.

E siamo arrivati ​​alla domanda iniziale: qual è il modo migliore per "memorizzare" quei record? Mi piace molto concentrare la discussione su questo e non "perché è necessario memorizzare i dati nella cache?"

+2

"Vogliamo evitare di interrogare il database il più possibile, quindi abbiamo deciso di mantenere alcuni dati sempre nella memoria." - hai utilizzato Traceview per confermare che si tratta di un problema per la tua app? "Vogliamo qualche aiuto/consiglio da parte tua" - il mio consiglio è: provare prima c'è un problema. Hai pochissima RAM con cui lavorare. Costruire qualche quadro importante per affrontare un problema inesistente sarebbe uno spreco di energie. Se hai dimostrato che questo è un problema, mi piacerebbe un puntatore al post del blog in cui l'hai scritto, dato che sono sempre interessato ai risultati dei test delle prestazioni. – CommonsWare

+0

@CommonsWare: Capisco il tuo punto e sono completamente d'accordo con te. Ma, come sviluppatori PalmOS e .NetCF, abbiamo già affrontato questo problema prima. In PalmOS tutti i dati sono in memoria in base alla progettazione (.pdb) e non si verificano problemi di prestazioni durante l'acquisizione dei dati. Nell'altra mano, in WM affrontiamo "il problema", e quindi abbiamo creato questa "soluzione" sopra elencata. Ma ora, in Android, desideriamo fare "nel modo giusto". Vogliamo sapere se stiamo per affrontare problemi di prestazioni quando si interrompe il database "ogni volta". Quindi abbiamo deciso di chiedere suggerimenti qui. Grazie comunque. – Christian

+2

"Vogliamo sapere se stiamo per affrontare problemi di prestazioni quando interrompiamo il database" ogni volta ", così abbiamo deciso di chiedere dei suggerimenti qui." -- No, tu non l'hai fatto. Vorrei che tu avessi. Invece, hai dichiarato che esisteva un problema ("non vogliamo interrogare sqlite ogni volta che abbiamo bisogno di qualche record") e volevi aiuto con la tua soluzione. Come con la maggior parte delle piattaforme, la risposta a se si "affronteranno problemi di prestazioni quando si interrompe il database" è "dipende dalle query". Fidati di me, solo perché c'è un problema su WM non significa che lo stesso problema esiste altrove. Usa Traceview. – CommonsWare

risposta

61

La maggior parte delle app sulla piattaforma (contatti, email, Gmail, calendario, ecc.) Non lo fa. Alcuni di questi hanno schemi di database estremamente complicati con potenzialmente una grande quantità di dati e non hanno bisogno di farlo. Quello che stai proponendo di fare è di causare il dolore enorme per te, senza alcun guadagno chiaro.

È necessario innanzitutto concentrarsi sulla progettazione del database e dello schema per poter eseguire query efficienti. Ci sono due ragioni principali per cui posso pensare che l'accesso al database sia lento:

  • Avete schemi dati davvero complicati.
  • Hai una grande quantità di dati.

Se hai molti dati, non puoi permetterti di tenere tutto in memoria comunque, quindi questo è un vicolo cieco. Se si dispone di strutture complicate, in entrambi i casi si trarrebbe vantaggio dall'ottimizzazione per migliorare le prestazioni. In entrambi i casi, lo schema del database sarà fondamentale per ottenere buone prestazioni.

realtà ottimizzare lo schema può essere un po 'una di magia nera (e non sono un esperto su di esso), ma alcune cose da guardare sono la creazione corretta indici sulle righe si interrogherà, progettazione si unisce in modo prenderanno percorsi efficienti, ecc. Sono sicuro che ci sono molte persone che possono aiutarti in quest'area.

Si potrebbe anche provare a guardare la fonte di alcuni dei database della piattaforma per ottenere alcune idee su come progettare per ottenere buone prestazioni. Ad esempio, il database dei contatti (in particolare a partire dalla versione 2.0) è estremamente complicato e offre molte ottimizzazioni per fornire buone prestazioni su dati relativamente grandi e insiemi di dati estensibili con un sacco di diversi tipi di query.

Aggiornamento:

Ecco un buon esempio di quanto sia importante l'ottimizzazione del database è. Nel database dei provider di contenuti multimediali di Android, una versione più recente della piattaforma ha modificato lo schema in modo significativo per aggiungere alcune nuove funzionalità. Il codice di aggiornamento per modificare un database multimediale esistente nel nuovo schema potrebbe impiegare 8 minuti o più per l'esecuzione.

Un ingegnere ha effettuato un'ottimizzazione che ha ridotto il tempo di aggiornamento di un vero database di test da 8 minuti a 8 secondi. Un miglioramento delle prestazioni 60x.

Qual è stata questa ottimizzazione?

Era per creare un indice temporaneo, nel punto di aggiornamento, su una colonna importante utilizzata nelle operazioni di aggiornamento.(E poi cancellarlo quando fatto.) Quindi questo miglioramento delle prestazioni 60x arriva anche se include anche il tempo necessario per costruire un indice su una delle colonne utilizzate durante l'aggiornamento.

SQLite è una di quelle cose in cui se si sa cosa si sta facendo può essere notevolmente efficiente. E se non ti preoccupi del modo in cui lo usi, puoi finire con una prestazione scadente. È una scommessa sicura, tuttavia, se si riscontrano problemi di prestazioni, è possibile correggerli migliorando il modo in cui si utilizza SQLite.

5

Il problema con una cache di memoria è ovviamente che è necessario mantenerlo sincronizzato con il database. Ho trovato che interrogare il database è in realtà piuttosto veloce, e potresti essere pre-ottimizzando qui. Abbiamo fatto molti test su query con set di dati diversi e non hanno mai impiegato più di 10-20 ms

Tutto dipende da come si stanno utilizzando i dati, naturalmente. Le ListView sono ottimizzate per gestire grandi numero di righe (ho testato nell'intervallo 5000 senza problemi reali.)

Se si sta con la cache di memoria, è possibile che il database avvisi la cache quando cambia il contenuto e quindi è possibile aggiorna la cache chiunque può aggiornare il database senza conoscere la memorizzazione nella cache. Inoltre, se si crea un ContentProvider sul proprio database, è possibile utilizzare ContentResolver per notificare le modifiche se si registra utilizzando registerContentObserver.

+0

Mi sento bene a conoscere quelle statistiche che hai detto. Sono abbastanza sicuro che stiamo pre-ottimizzando, ma è perché la nostra esperienza in passato con SQLite e dispositivi mobili (WM). La sincronizzazione tra memoria e database non è ciò di cui ci preoccupiamo, perché lo stesso "tempo" cambia qualcosa che pubblicheremo sul database. La cache sarà globale, quindi tutte le app vedranno la stessa cosa. E tutte le modifiche verranno apportate prima nella memoria e quindi nel database (in un'operazione "atomica", implementata in quelle classi). – Christian

Problemi correlati