Published by

Il y a 13 ans -

Temps de lecture 6 minutes

Booster vos recherches avec Oracle Coherence

Oracle Coherence est une solution de Data Grid. Elle permet de constituer des grilles de données à l’aide de 4 types de caches:

  • Cache distribué: l’ensemble des données est réparti sur les différents nœuds qui composent le cluster Coherence. Afin de garantir une bonne tolérance aux pannes, les données peuvent être sauvegardées sur un ou plusieurs nœuds du cluster. Cette typologie est extrêmement extensible : il suffit d’ajouter des nouveaux nœuds pour augmenter la capacité globale du cache.
  • Cache répliqué: l’ensemble des données est répliqué sur l’ensemble des nœuds du cluster. La modification d’une entrée sera propagée à l’ensemble des nœuds. Cette typologie permet d’offrir un accès très rapide en lecture car un seul nœud est sollicité dans l’opération. La contre-partie est que les opérations d’écriture sont lentes et que la taille du cache est indépendante du nombre de nœuds qui composent le cluster.
  • Cache local: les données sont conservées exclusivement sur la JVM.
  • Near Cache: il permet d’offrir le meilleur des caches de type ‘Répliqué’ (Performance) et de type ‘Distribué’ (Extensibilité) en fournissant un accès rapide aux données accédées le plus fréquemment et le plus récemment. Il est composé de 2 parties:
    • le ‘front-cache’ pour les accès locaux, de petite taille, typiquement un cache Local (Local Cache),
    • le ‘back-cache’ un cache de plus grande envergure, typiquement un cache distribué (Distributed Cache), qui contient l’ensemble des données avec leurs éventuelles sauvegardes.

Le point fort d’Oracle Coherence est de proposer la même API quelque soit le type de cache. Il est donc possible, par simple configuration, de modifier le type de cache et de l’adapter en fonction des besoins ou de l’environnement: cache local en « Développement », cache distribué en « Intégration », « Near » cache en « Production ».

En première approche, l’API d’Oracle Coherence est extrêmement simple : elle se résume à obtenir une Map et à la manipuler par les opérations habituelles: put(), get(), keySet(), entrySet()….

import com.tangosol.net.CacheFactory;

Map cache = CacheFactory.getCache("moncache");        //1. Récupération d'une instance de cache,
cache.put("benoit","moussaud");                       //2. Ajout d'un élément dans le cache
String name = (String)cache.get("benoit");            //3. Recherche par sa clé d'un élément dans le cache

assert name.equals("moussaud");

Le type de cache est déduit du nom du cache. Il est configuré dans le fichier coherence-cache-config.xml inclus dans coherence.jar. Par défaut, les règles suivantes sont appliquées:

Nom du cache Type
dist-* distribué
near-* near cache
repl-* repliqué
local-* local
sinon distribué

Si l’on veut appliquer ses propres règles, il faut définir son propre fichier de configuration et indiquer son emplacement au lancement de Coherence avec la propriété système tangosol.coherence.cacheconfig.

La suite de l’article va montrer comment interroger un cache Coherence autrement que par une clé dans une Map.

Un service de recherche de communes…

Prenons un service qui permet de rechercher l’ensemble des communes françaises en précisant le début de leur nom.

public interface CatalogService {
	public List getCommuneByName(String name);
}

Une implémentation classique est de créer une table ‘COMMUNE’ dans une base de données, d’y insérer les ensembles des communes de France (38206 dans mon exemple) avec leur libellé, un code postal et un code Insee, et d’effectuer la requête SQL SELECT ID, DEP_CODE, CODE_INSEE, LIBELLE FROM COMMUNE where LIBELLE like #name#.

Voici le résultat d’un benchmark du service avec pour paramètre la première lettre de la commune (de A à Z)

Résultat du benchmark Base de données
Benchmark Base de données

Le temps de réponse du service est mauvais et proportionnel au nombre de communes retournées

…Coherence à la rescousse…

La première idée est de placer le résultat de la recherche dans un cache avec pour clé le paramètre de recherche. Ce type de conception est efficace seulement si le nombre de filtres est fini, par exemple s’il résulte d’une sélection d’une combo-box dans une page web. Si la valeur du filtre est moins contrainte, le service sera appelé par un grand nombre de filtres différents et donc le cache risque d’être peu efficace (à moins de disposer d’une mémoire infinie de manière à conserver toutes les requêtes !).

La seconde idée est de charger dans un cache l’ensemble des communes et d’effectuer une recherche directement dans le cache. L’API Coherence offre la notion de Filter

public class CatalogServiceWithCoherence implements CatalogService,
  
     private final NamedCache cacheCommunes = CacheFactory.getCache("Commune");

     public List getCommuneByName(String name) {
         Filter filter = new LikeFilter("getName", name + "%", false);
         return performSearch(filter);
     }

     @SuppressWarnings("unchecked")
     private List performSearch(Filter filter) {
         Set> entrySet=cacheCommunes
                 .entrySet(filter);

         List result = new ArrayList(entrySet.size());
         for (Map.Entry e : entrySet) {
             result.add(e.getValue());
         }

         return result;
     }
}

Appliquons le même benchmark.

Résultat du benchmark Coherence avec Filtre
Résultat du benchmark Coherence avec Filtre

Le temps de réponse du service est meilleur (autour de 500 ms) mais constant. Il y a donc un gain par rapport aux requêtes qui retournent beaucoup d’éléments (A,B,C,L,S,..) mais une perte sur les requêtes qui retournent peu d’éléments. Donc plus le filtre sera sélectif, moins la recherche dans le cache sera efficace.

Ceci se comprend mieux si on affine l’étude de l’API Coherence. L’interface Filter définit une seule méthode: evaluate

package com.tangosol.util;

public abstract interface Filter
{
  public abstract boolean evaluate(Object paramObject);
}

Donc une recherche dans le cache par filtre se résume à parcourir l’ensemble des entrées de la Map et à appliquer le filtre passé en paramètre.

…Encore plus rapide avec un index

Si on examine de plus près le filtre utilisé LikeFilter » >LikeFilter, on peut noter qu’il implémente l’interface IndexAwareFilter. Il est donc possible d’indexer le contenu d’un cache Coherence. Le code ci-dessous ajoute un index au cache « IndexedCommune »: pour chaque entrée ajoutée au cache, il va extraire et indexer la valeur de la propriété « Name » (getName()).

NamedCache cache = CacheFactory.getCache("IndexedCommune");
cache.addIndex(new ReflectionExtractor("getName"), true, null);

Appliquons le benchmark.

Benchmark Coherence avec filtre indexé
Benchmark Coherence avec filtre indexé

Avec l’ajout de l’index, le temps de réponse du service a encore nettement chuté et reste proportionnel au nombre de communes retourné. Donc si le filtre est sélectif, le temps de réponse sera très bon.

En moyenne, la première solution a divisé le temps de réponse par 5, la seconde par 80. Cependant il faut garder à l’esprit qu’un accès à une valeur par son index (cache.get(key)) reste beaucoup plus performant que l’utilisation des Filters. Leurs utilisations doivent rester des exceptions.

Conclusion

Les solutions de « Data Grid » modernes comme Oracle Coherence offrent maintenant des services de haut niveau, semblables à ceux offerts par des SGBD, comme la gestion de la concurrence, des transactions, des indexs, du partitionnement, qui permettent d’améliorer nettement les performances d’une application J2EE.

Sources

Vous retrouverez l’ensemble des sources présentées dans ce billet dans le repository SVN de Xebia France.

Références

Published by

Commentaire

4 réponses pour " Booster vos recherches avec Oracle Coherence "

  1. Published by , Il y a 13 ans

    Quelle est la BD utilisée ?
    Est-il possible de faire un test avec des BD mémoire telles que TimesTen et JavaDB ?

    Un bon article toutefois.

  2. Published by , Il y a 13 ans

    Dans le test, la BD est Oracle 10g. Je n’ai pas à ma disposition TimeTen donc pas possible de faire le test. Vue la volumétrié du benchmark, il semble clair que JavaDB pour être un bon candidat. Dans le cadre d’une volumétrie beaucoup plus importante, une solution Data Grid Distribué est plus adaptée car plus extensible (scalable)

    Merci pour votre retour

  3. Published by , Il y a 13 ans

    Je trouve étonnant dans ce bench que l’on mette douze secondes pour récupérer les communes qui commencent par « S ».
    Peut-être manque-t-il un index Oracle sur la colonne « commune » ?
    Moi j’ai fait un test avec une database Oracle 10.2.0.4 et cela me répond en 63ms pour les communes commençant par « S »…

Laisser un commentaire

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

Nous recrutons

Être un Sapient, c'est faire partie d'un groupe de passionnés ; C'est l'opportunité de travailler et de partager avec des pairs parmi les plus talentueux.