25

Nous concevons une application Android qui a beaucoup de données ("clients", "produits", "commandes" ...), et nous don ne veux pas interroger SQLite chaque fois que nous avons besoin d'un enregistrement. Nous voulons éviter d'interroger la base de données autant que possible, nous avons donc décidé de garder certaines données toujours en mémoire.Meilleure pratique pour conserver les données en mémoire et la base de données en même temps sur Android

Notre idée initiale est de créer deux classes simples:

  1. "MemoryRecord": une classe qui contiendra essentiellement un tableau d'objets (string, int, double, datetime, etc ...), ce sont les données d'un enregistrement de table, et toutes les méthodes pour obtenir ces données dans/hors de ce tableau. "MemoryTable": classe qui contiendra essentiellement une carte de [Key, MemoryRecord] et toutes les méthodes pour manipuler cette Map et insérer/mettre à jour/supprimer des enregistrements dans/de la base de données.

Ces classes seront dérivées à chaque type de table que nous avons dans la base de données. Bien sûr, il existe d'autres méthodes utiles non énumérées ci-dessus, mais elles ne sont pas importantes à ce stade. Ainsi, au démarrage de l'application, nous chargerons ces tables d'une base de données SQLite en mémoire en utilisant ces classes, et chaque fois que nous aurons besoin de changer certaines données, nous changerons en mémoire et les publierons dans la base de données juste après. Mais, nous voulons de l'aide/des conseils de votre part. Pouvez-vous suggérer quelque chose de plus simple ou efficace pour mettre en œuvre une telle chose? Ou peut-être quelques classes existantes qui le font déjà pour nous?

Je comprends ce que vous essayez de me montrer, et je vous en remercie. Mais, disons que nous avons une table avec 2000 enregistrements, et j'ai besoin de lister ces enregistrements. Pour chacun d'entre eux, je dois interroger 30 autres tables (certaines avec 1000 enregistrements, d'autres avec 10 enregistrements) pour ajouter des informations supplémentaires dans la liste, et ce pendant qu'il "vole" (et comme vous le savez, nous devons être très rapides à ce moment là).

Maintenant, vous allez dire: "construisez simplement votre requête principale avec toutes ces" jointures ", et apportez tout ce dont vous avez besoin en une étape SQLite peut être très rapide, si votre base de données est bien conçue, etc. .. ".

OK, mais cette requête deviendra très compliquée et sûre, même si SQLite est très rapide, elle sera "trop" lente (2 à 4 secondes, comme je l'ai confirmé, et ce n'est pas un moment acceptable pour nous). Un autre complicateur est que, en fonction de l'interaction de l'utilisateur, nous devons "re-interroger" tous les enregistrements, car les tables impliquées ne sont pas identiques et nous devons "rejoindre" un autre ensemble de tables. Donc, une alternative est d'apporter seulement les enregistrements principaux (cela ne changera jamais, peu importe ce que fait ou veut l'utilisateur) sans jointure (c'est très rapide!) Et interroger les autres tables chaque fois que nous voulons des données. Notez que sur la table avec 10 enregistrements seulement, nous allons chercher les mêmes enregistrements plusieurs fois et plusieurs fois. Dans ce cas, c'est une perte de temps, car peu importe SQLite rapide, il sera toujours plus coûteux de faire des requêtes, des curseurs, des fetchs, etc ... que de simplement saisir l'enregistrement d'une sorte de "cache mémoire". Je tiens à préciser que nous n'avons pas l'intention de conserver toutes les données en mémoire, seulement quelques tables que nous interrogeons très souvent.

Et nous sommes arrivés à la question initiale: Quelle est la meilleure façon de "mettre en cache" ces enregistrements? J'aime vraiment centrer la discussion sur cela et non pas "pourquoi avez-vous besoin de mettre en cache des données?"

+2

"Nous voulons éviter d'interroger autant que possible la base de données, nous avons donc décidé de conserver certaines données en mémoire." - avez-vous utilisé Traceview pour confirmer qu'il s'agit d'un problème pour votre application? "Nous voulons de l'aide/des conseils de votre part" - mon conseil est: prouver qu'il y a un problème en premier. Vous avez très peu de RAM avec laquelle travailler. Construire un grand cadre pour faire face à un problème inexistant serait un gaspillage d'efforts. Si vous avez démontré que c'est un problème, j'aimerais un pointeur sur l'article de blog où vous l'avez rédigé, car je suis toujours intéressé par les résultats des tests de performance. – CommonsWare

+0

@CommonsWare: Je comprends votre point de vue et je suis entièrement d'accord avec vous. Mais, en tant que développeurs PalmOS et .NetCF, nous sommes déjà confrontés à ce problème auparavant. Dans PalmOS, toutes les données sont stockées dans la mémoire (.pdb) et il n'y a aucun problème de performance lors de l'obtention de données. D'un autre côté, dans WM, nous sommes confrontés au «problème», puis nous avons créé cette «solution» listée ci-dessus. Mais maintenant, sous Android, nous souhaitons faire de la "bonne manière". Nous voulons savoir si nous allons faire face à des problèmes de performances lorsque nous demandons à la base de données "à chaque fois". Nous avons donc décidé de demander des suggestions ici. Merci quand même. – Christian

+2

"Nous voulons savoir si nous allons devoir faire face à des problèmes de performances lorsque nous demandons des bases de données à chaque fois" Nous avons donc décidé de demander des suggestions ici. " -- non tu ne l'as pas fait. Je voudrais que tu l'aies. Au lieu de cela, vous avez déclaré qu'un problème existait ("nous ne voulons pas interroger sqlite à chaque fois que nous avons besoin d'un enregistrement") et que vous vouliez de l'aide pour votre solution. Comme pour la plupart des plates-formes, la réponse à la question "si vous allez faire face à des problèmes de performances lors d'une requête de base de données" est "cela dépend des requêtes". Croyez-moi, juste parce qu'il y a un problème sur WM ne signifie pas le même problème existe ailleurs. Utilisez Traceview. – CommonsWare

Répondre

61

La grande majorité des applications sur la plate-forme (contacts, e-mail, Gmail, calendrier, etc.) ne le font pas. Certains d'entre eux ont des schémas de base de données extrêmement compliqués avec potentiellement une grande quantité de données et n'ont pas besoin de le faire. Qu'est-ce que vous proposez de faire va causer énorme douleur pour vous, sans gain clair.

Vous devez d'abord vous concentrer sur la conception de votre base de données et de votre schéma afin de pouvoir effectuer des requêtes efficaces. Il y a deux raisons principales que je peux penser pour l'accès base de données pour être lente:

  • Vous avez vraiment compliqué les schémas de données.
  • Vous avez une très grande quantité de données.

Si vous avez beaucoup de données, vous ne pouvez pas tout garder en mémoire, c'est donc une impasse. Si vous avez des structures compliquées, vous bénéficieriez dans les deux cas d'une optimisation pour améliorer les performances. Dans les deux cas, votre schéma de base de données va être la clé de bonnes performances.

En fait, optimiser le schéma peut être un peu un art noir (et je ne suis pas expert en la matière), mais certaines choses à surveiller créent correctement des index sur les lignes que vous interrogerez. chemins efficaces, etc. Je suis sûr qu'il y a beaucoup de gens qui peuvent vous aider dans ce domaine.

Vous pouvez également essayer de trouver la source de certaines bases de données de la plate-forme pour avoir des idées sur la façon de concevoir de bonnes performances. Par exemple, la base de données Contacts (en particulier à partir de la version 2.0) est extrêmement compliquée et a beaucoup d'optimisations pour fournir de bonnes performances sur des données relativement volumineuses et des ensembles de données extensibles avec beaucoup de différents types de requêtes.

Mise à jour:

est ici une bonne illustration de la façon dont l'optimisation de la base de données est importante. Dans la base de données des fournisseurs de médias d'Android, une version plus récente de la plateforme a considérablement modifié le schéma pour ajouter de nouvelles fonctionnalités. Le code de mise à niveau pour modifier une base de données de médias existante vers le nouveau schéma peut prendre 8 minutes ou plus à exécuter.

Un ingénieur a effectué une optimisation qui a réduit le temps de mise à niveau d'une base de données de test réelle de 8 minutes à 8 secondes. Une amélioration de la performance de 60x.

Quelle était cette optimisation?

Il s'agissait de créer un index temporaire, au moment de la mise à niveau, sur une colonne importante utilisée dans les opérations de mise à niveau.(Et puis supprimez-le lorsque vous avez terminé.) Donc, cette amélioration des performances 60x vient même si elle comprend également le temps nécessaire pour construire un index sur l'une des colonnes utilisées lors de la mise à niveau. SQLite est l'une de ces choses où si vous savez ce que vous faites, il peut être remarquablement efficace. Et si vous ne faites pas attention à la façon dont vous l'utilisez, vous pouvez vous retrouver avec des performances misérables. Si vous rencontrez des problèmes de performances, vous pouvez parier que vous pouvez les corriger en améliorant la façon dont vous utilisez SQLite.

5

Le problème avec un cache mémoire est bien sûr que vous devez le synchroniser avec la base de données.J'ai trouvé que l'interrogation de la base de données est en fait assez rapide, et vous pouvez pré-optimiser ici. Nous avons fait beaucoup de tests sur des requêtes avec des ensembles de données différents et ils ne prennent jamais plus de 10 à 20 ms

Tout dépend de la façon dont vous utilisez les données, bien sûr. nombre de lignes (j'ai testé dans la gamme 5000 sans problèmes réels)

Si vous allez rester avec le cache mémoire, vous pouvez avoir la base de données notifier le cache lorsque son contenu change et vous pouvez mettre à jour le cache. n'importe qui peut mettre à jour la base de données sans connaître la mise en cache. En outre, si vous construisez un ContentProvider sur votre base de données, vous pouvez utiliser ContentResolver pour vous notifier des modifications si vous vous inscrivez en utilisant registerContentObserver.

+0

Je me sens bien de connaître ces statistiques que vous avez dites. Je suis sûr que nous sommes en train de pré-optimiser, mais c'est parce que nous avons déjà travaillé avec SQLite e mobile devices (WM). La synchronisation entre la mémoire et la base de données n'est pas ce qui nous inquiète, parce que le même "temps" nous changeons quelque chose que nous l'afficherons dans la base de données. Le cache sera global, donc toutes les applications verront la même chose. Et tous les changements seront faits d'abord en mémoire et donc dans la base de données (dans une opération "atomique", implémentée dans ces classes). – Christian