Redis
 sql >> Datenbank >  >> NoSQL >> Redis

Warum ist es mit 100.000 Datensätzen so langsam, wenn die Pipeline in Redis verwendet wird?

Es gibt ein paar Punkte, die Sie berücksichtigen müssen, bevor Sie einen solchen Benchmark schreiben (und insbesondere einen Benchmark, der die JVM verwendet):

  • Auf den meisten (physischen) Maschinen kann Redis mehr als 100.000 ops/s verarbeiten, wenn Pipelining verwendet wird. Ihr Benchmark behandelt nur 100.000 Elemente, sodass er nicht lange genug dauert, um aussagekräftige Ergebnisse zu erzielen. Außerdem bleibt keine Zeit, um die aufeinanderfolgenden Phasen des JIT einzuleiten.

  • die absolute Zeit ist keine sehr relevante Metrik. Eine bessere und stabilere Metrik wäre es, den Durchsatz anzuzeigen (d. h. die Anzahl der Vorgänge pro Sekunde), während der Benchmark mindestens 10 Sekunden lang ausgeführt wird.

  • Ihre innere Schleife erzeugt eine Menge Müll. Wenn Sie vorhaben, Jedis+Redis zu benchmarken, müssen Sie den Overhead Ihres eigenen Programms gering halten.

  • Da Sie alles in der Hauptfunktion definiert haben, wird Ihre Schleife nicht vom JIT kompiliert (abhängig von der von Ihnen verwendeten JVM). Nur die inneren Methodenaufrufe dürfen sein. Wenn Sie möchten, dass das JIT effizient ist, stellen Sie sicher, dass Sie Ihren Code in Methoden kapseln, die vom JIT kompiliert werden können.

  • Optional möchten Sie vielleicht eine Aufwärmphase hinzufügen, bevor Sie die eigentliche Messung durchführen, um den Aufwand für die Ausführung der ersten Iterationen mit dem Barebone-Interpreter und die Kosten für das JIT selbst zu vermeiden.

Was das Redis-Pipelining betrifft, ist Ihre Pipeline viel zu lang. 100.000 Befehle in der Pipeline bedeuten, dass Jedis einen 6-MB-Puffer aufbauen muss, bevor er etwas an Redis senden kann. Dies bedeutet, dass die Socket-Puffer (auf der Client-Seite und vielleicht auf der Server-Seite) gesättigt sind und dass Redis auch mit 6 MB Kommunikationspuffern umgehen muss.

Darüber hinaus ist Ihr Benchmark immer noch synchron (die Verwendung einer Pipeline macht ihn nicht auf magische Weise asynchron). Mit anderen Worten, Jedis beginnt nicht mit dem Lesen von Antworten, bis die letzte Abfrage Ihrer Pipeline an Redis gesendet wurde. Wenn die Pipeline zu lang ist, kann sie Dinge blockieren.

Erwägen Sie, die Größe der Pipeline auf 100–1000 Vorgänge zu begrenzen. Natürlich werden mehr Roundtrips generiert, aber der Druck auf den Kommunikationsstack wird auf ein akzeptables Niveau reduziert. Betrachten Sie zum Beispiel das folgende Programm:

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

Mit diesem Programm können Sie mit oder ohne Pipelining testen. Achten Sie darauf, die Anzahl der Iterationen (N-Parameter) zu erhöhen, wenn Pipelining verwendet wird, sodass es mindestens 10 Sekunden lang ausgeführt wird. Wenn Sie das println in der Schleife auskommentieren, werden Sie feststellen, dass das Programm am Anfang langsam ist und schneller wird, wenn das JIT beginnt, Dinge zu optimieren (deshalb sollte das Programm mindestens einige Sekunden laufen, um ein aussagekräftiges Ergebnis zu liefern).

Auf meiner Hardware (einer alten Athlon-Box) kann ich einen 8- bis 9-mal höheren Durchsatz erzielen, wenn die Pipeline verwendet wird. Das Programm könnte weiter verbessert werden, indem die Schlüssel/Wert-Formatierung in der inneren Schleife optimiert und eine Aufwärmphase hinzugefügt wird.