Redis
 sql >> Database >  >> NoSQL >> Redis

perché è così lento con 100.000 record quando si utilizza la pipeline in redis?

Ci sono alcuni punti che devi considerare prima di scrivere un tale benchmark (e soprattutto un benchmark che utilizza JVM):

  • sulla maggior parte delle macchine (fisiche), Redis è in grado di elaborare più di 100.000 operazioni al secondo quando viene utilizzato il pipelining. Il tuo benchmark riguarda solo 100.000 articoli, quindi non dura abbastanza a lungo per produrre risultati significativi. Inoltre, non c'è tempo per l'avvio delle fasi successive della JIT.

  • il tempo assoluto non è una metrica molto rilevante. Visualizzare il throughput (ovvero il numero di operazioni al secondo) mantenendo il benchmark in esecuzione per almeno 10 secondi sarebbe una metrica migliore e più stabile.

  • il tuo ciclo interiore genera molta spazzatura. Se hai intenzione di confrontare Jedis+Redis, devi mantenere basso il sovraccarico del tuo programma.

  • poiché hai definito tutto nella funzione principale, il tuo ciclo non verrà compilato dalla JIT (a seconda della JVM che usi). Solo le chiamate al metodo interno possono esserlo. Se vuoi che il JIT sia efficiente, assicurati di incapsulare il tuo codice in metodi che possono essere compilati dal JIT.

  • facoltativamente, potresti voler aggiungere una fase di riscaldamento prima di eseguire la misurazione effettiva per evitare di tenere conto dell'overhead dell'esecuzione delle prime iterazioni con l'interprete bare-bone e del costo della JIT stessa.

Ora, per quanto riguarda la pipeline Redis, la tua pipeline è troppo lunga. 100.000 comandi in cantiere significano che Jedis deve creare un buffer da 6 MB prima di inviare qualsiasi cosa a Redis. Significa che i buffer dei socket (sul lato client e forse sul lato server) saranno saturi e che Redis dovrà gestire anche buffer di comunicazione da 6 MB.

Inoltre, il tuo benchmark è ancora sincrono (l'uso di una pipeline non lo rende magicamente asincrono). In altre parole, Jedis non inizierà a leggere le risposte fino a quando l'ultima query della tua pipeline non sarà stata inviata a Redis. Quando la pipeline è troppo lunga, può potenzialmente bloccare le cose.

Considerare di limitare la dimensione della pipeline a 100-1000 operazioni. Naturalmente, genererà più roundtrip, ma la pressione sullo stack di comunicazione sarà ridotta a un livello accettabile. Ad esempio, considera il seguente programma:

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);
   }
 }

Con questo programma, puoi testare con o senza pipeline. Assicurati di aumentare il numero di iterazioni (parametro N) quando viene utilizzato il pipelining, in modo che venga eseguito per almeno 10 secondi. Se rimuovi il commento da println nel ciclo, ti renderai conto che il programma è lento all'inizio e diventerà più veloce man mano che il JIT inizia a ottimizzare le cose (ecco perché il programma dovrebbe essere eseguito almeno alcuni secondi per dare un risultato significativo).

Sul mio hardware (una vecchia scatola Athlon), posso ottenere 8-9 volte più throughput quando viene utilizzata la pipeline. Il programma potrebbe essere ulteriormente migliorato ottimizzando la formattazione chiave/valore nel ciclo interno e aggiungendo una fase di riscaldamento.