Redis
 sql >> Base de données >  >> NoSQL >> Redis

pourquoi est-il si lent avec 100 000 enregistrements lors de l'utilisation de pipeline dans redis ?

Il y a quelques points que vous devez considérer avant d'écrire un tel benchmark (et surtout un benchmark utilisant la JVM) :

  • sur la plupart des machines (physiques), Redis est capable de traiter plus de 100 000 opérations/s lorsque le pipelining est utilisé. Votre référence ne traite que des éléments de 100 000, elle ne dure donc pas assez longtemps pour produire des résultats significatifs. De plus, les étapes successives du JIT n'ont pas le temps de démarrer.

  • le temps absolu n'est pas une mesure très pertinente. Afficher le débit (c'est-à-dire le nombre d'opérations par seconde) tout en maintenant le benchmark en cours d'exécution pendant au moins 10 secondes serait une mesure meilleure et plus stable.

  • votre boucle interne génère beaucoup de déchets. Si vous envisagez de comparer Jedis + Redis, vous devez limiter les frais généraux de votre propre programme.

  • comme vous avez tout défini dans la fonction main, votre boucle ne sera pas compilée par le JIT (selon la JVM que vous utilisez). Seuls les appels de méthode internes peuvent l'être. Si vous voulez que le JIT soit efficace, assurez-vous d'encapsuler votre code dans des méthodes pouvant être compilées par le JIT.

  • éventuellement, vous pouvez ajouter une phase d'échauffement avant d'effectuer la mesure proprement dite pour éviter de comptabiliser les frais généraux liés à l'exécution des premières itérations avec l'interpréteur simple et le coût du JIT lui-même.

Maintenant, en ce qui concerne le pipeline Redis, votre pipeline est bien trop long. 100 000 commandes dans le pipeline signifient que Jedis doit créer un tampon de 6 Mo avant d'envoyer quoi que ce soit à Redis. Cela signifie que les tampons de socket (côté client et peut-être côté serveur) seront saturés et que Redis devra également gérer des tampons de communication de 6 Mo.

De plus, votre benchmark est toujours synchrone (l'utilisation d'un pipeline ne le rend pas asynchrone comme par magie). En d'autres termes, Jedis ne commencera pas à lire les réponses tant que la dernière requête de votre pipeline n'aura pas été envoyée à Redis. Lorsque le pipeline est trop long, il a le potentiel de bloquer des choses.

Envisagez de limiter la taille du pipeline à 100-1000 opérations. Bien sûr, cela générera plus d'allers-retours, mais la pression sur la pile de communication sera réduite à un niveau acceptable. Par exemple, considérez le programme suivant :

import redis.clients.jedis.*;
import java.util.*;

public class TestPipeline {

    /**
     * @param args
     */

    int i = 0; 
    Map<String, String> map = new HashMap<String, String>();
    ShardedJedis jedis;  

    // Number of iterations
    // Use 1000 to test with the pipeline, 100 otherwise
    static final int N = 1000;

    public TestPipeline() {
      JedisShardInfo si = new JedisShardInfo("127.0.0.1", 6379);
      List<JedisShardInfo> list = new ArrayList<JedisShardInfo>();
      list.add(si);
      jedis = new ShardedJedis(list);
    } 

    public void push( int n ) {
     ShardedJedisPipeline pipeline = jedis.pipelined();
     for ( int k = 0; k < n; k++) {
      map.put("id", "" + i);
      map.put("name", "lyj" + i);
      pipeline.hmset("m" + i, map);
      ++i;
     }
     pipeline.sync(); 
    }

    public void push2( int n ) {
     for ( int k = 0; k < n; k++) {
      map.put("id", "" + i);
      map.put("name", "lyj" + i);
      jedis.hmset("m" + i, map);
      ++i;
     }
    }

    public static void main(String[] args) {
      TestPipeline obj = new TestPipeline();
      long startTime = System.currentTimeMillis();
      for ( int j=0; j<N; j++ ) {
       // Use push2 instead to test without pipeline
       obj.push(1000); 
       // Uncomment to see the acceleration
       //System.out.println(obj.i);
     }
     long endTime = System.currentTimeMillis();
     double d = 1000.0 * obj.i;
     d /= (double)(endTime - startTime);
     System.out.println("Throughput: "+d);
   }
 }

Avec ce programme, vous pouvez tester avec ou sans pipelining. Assurez-vous d'augmenter le nombre d'itérations (paramètre N) lorsque le pipelining est utilisé, afin qu'il s'exécute pendant au moins 10 secondes. Si vous décommentez le println dans la boucle, vous vous rendrez compte que le programme est lent au début et s'accélérera au fur et à mesure que le JIT commencera à optimiser les choses (c'est pourquoi le programme doit s'exécuter au moins plusieurs secondes pour donner un résultat significatif).

Sur mon matériel (une ancienne boîte Athlon), je peux obtenir 8 à 9 fois plus de débit lorsque le pipeline est utilisé. Le programme pourrait être encore amélioré en optimisant le formatage clé/valeur dans la boucle interne et en ajoutant une phase d'échauffement.