Database : Indexer ses tables pour le cache applicatif

L’utilisation massive d’internet et le besoin de montée en charge qui en a suivi a changé le mode de développement des applications et a vu se diffuser un ensemble de nouvelles techniques de développement comme la mise en cache des données du coté applicatif directement en mémoire ou avec un cache distribué plus (Oracle Coherence) ou moins (Memcached) évolué.
Le but de ce cache applicatif est de décharger la base de données en travaillant sur des données en mémoire locale ou distribuée et non pas d’aller les  chercher dans la base.

Le principe est ainsi :

  • mon objet identifié par un id est-il présent en mémoire ?
  • oui : je le récupère dans le cache applicatif ;
  • non : je vais le chercher en base.

Jusque là, facile. Malheureusement, la plupart du temps, on ne connait pas l’id. Par exemple si j’ai besoin d’informations sur un utilisateur, ma recherche va porter sur son nom et non pas sur son id.  Donc ce que je voudrais faire serait :

  • récupérer l’id, et uniquement l’id, par l’entièreté des informations sur mon utilisateurs, sinon le cache n’a plus d’intérêt;
  • un objet identifié par cet id est-il présent dans le cache applicatif ?
  • oui : je le récupère dans le cache applicatif ;
  • non : je vais le chercher en base.

Problème, la base Oracle étant organisée en ligne,  si j’exécute une requête du style :
[sourcecode language= »sql »]
select id from myusertable t where name =’yooo13′
[/sourcecode]
pour récupérer l’id du tuple et le comparer avec le cache local,  il va falloir aller lire la ligne entièrement, même si j’ai créé un index sur la colonne, comme on peut le voir dans le plan suivant (table access by index rowid) :
[sourcecode language= »sql »]
————————————————————————
| Id | Operation | Name | Rows | Bytes | Cost |
————————————————————————
| 0 | SELECT STATEMENT | | 1 | 47 | 2 |
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 47 | 2 |
| 2 | INDEX RANGE SCAN | IX_MUT_N | 1 | | 1 |
————————————————————————
[/sourcecode]
On se retrouve alors avec un cache applicatif qui ne sert à rien car l’id ne joue pas le rôle de pointeur. Pour résoudre le problème, il y a deux solutions :

  • utiliser les rowid de la table :

[sourcecode language= »sql »]
select rowid from myusertable t where name =’yooo13′
[/sourcecode]

  • créer un index composite avec la colonne name et la colonne id :

[sourcecode language= »sql »]
create index IX_MUT_NI on myusertable (name,id) ;
[/sourcecode]
Dans les deux cas on obtient le plan suivant où l’on peut voir qu’il n’y a que l’index qui est utilisé :
[sourcecode language= »sql »]
————————————————————-
| Id | Operation | Name | Rows | Bytes | Cost |
————————————————————-
| 0 | SELECT STATEMENT | | 1 | 47 | 2 |
| 1 | INDEX RANGE SCAN| IX_MUT_NI| 1 | 47 | 2 |
————————————————————-
[/sourcecode]
Il suffit ensuite de tester si un objet existe déjà dans le cache pour savoir si on a besoin d’aller lire la ligne complètement dans la base.
Personnellement je préfère la solution 2 avec l’index composite car on n’est jamais à l’abri d’un changement de ligne et mapper des rowids dans des framework comme JPA ne doit pas être aisé.

2 réflexions sur “Database : Indexer ses tables pour le cache applicatif”

  1. Excusez mon ignorance mais pourriez-vous svp développer votre paragraphe « pour récupérer l’id du tuple et le comparer avec le cache local, il va falloir aller lire la ligne entièrement, même si j’ai créé un index sur la colonne, comme on peut le voir dans le plan suivant  » car je vous avoue ne pas comprendre ce qui vous fait affirmer cela en vous appuyant sur le plan d’execution qui suit immédiatement ce paragraphe ? Un grand merci à vous.

    1. Si je veux mettre un objet dans le cache je vais faire un put(id, myobject).
      Si je veux savoir si un objet existe dans le cache, je vais faire un get(id).
      => le but c’est de travailler avec des pointeur (id) et non pas l’objet entier.
      si dans le plan on a un « TABLE ACCESS BY INDEX ROWID » ca veut dire qu’on accède à la ligne entièrement et non pas uniquement à l’id, et ce n’est pas ce qu’on veux. ce qu’on veux c’est lire l’information dans la base uniquement quand on en a besoin. il ne faut pas avoir à lire l’information complète dans la base pour savoir si on doit le mettre en cache ou pas : on perd tout le bénéfice du cache.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *