2013-05-22 3 views
8

Es wird gesagt, dass pipeline ein besserer Weg ist, wenn viele set/get in redis erforderlich ist, so ist dies mein Testcode:Warum ist es so langsam mit 100.000 Datensätze bei der Verwendung von Pipeline in Redis?

public class TestPipeline { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     // TODO Auto-generated method stub 

     JedisShardInfo si = new JedisShardInfo("127.0.0.1", 6379); 
     List<JedisShardInfo> list = new ArrayList<JedisShardInfo>(); 
     list.add(si); 
     ShardedJedis jedis = new ShardedJedis(list); 
     long startTime = System.currentTimeMillis(); 
     ShardedJedisPipeline pipeline = jedis.pipelined(); 
     for (int i = 0; i < 100000; i++) { 
      Map<String, String> map = new HashMap<String, String>(); 
      map.put("id", "" + i); 
      map.put("name", "lyj" + i); 
      pipeline.hmset("m" + i, map); 
     } 
     pipeline.sync(); 
     long endTime = System.currentTimeMillis(); 
     System.out.println(endTime - startTime); 
    } 
} 

Als ich es lief, gibt es keine Antwort mit diesem Programm für eine Weile, aber wenn ich nicht mit pipe arbeite, dauert es nur 20073 ms, also bin ich verwirrt, warum es noch besser ist ohne pipeline und wie eine breite Lücke!

Danke für die Antwort, ein paar Fragen, wie berechnen Sie 6MB Daten? Wenn ich 10K Daten sende, ist die Pipeline immer schneller als der normale Modus, aber mit 100k, Pipeline würde keine Antwort.Ich denke, 100-1000 Operationen ist eine empfehlenswerte Wahl wie unten gesagt.Its gibt es mit JIT, da ich es nicht verstehe ?

+0

Auch 20 Sekunden klingt wie eine lange Zeit. Ich nehme an, eine Pipe ermöglicht das Senden asynchroner Updates und ohne eine Pipe sind die Updates synchron. –

+3

+1 Sie haben mich inspiriert, diesen Test zu schreiben [Fügen Sie eine Million in die Chronik ein] (https://github.com/peter-lawrey/Java-Chronicle/blob/master/chronicle/src/test/java/com/higherfrequencytrading/ Chronik/Beispiele/TestManyUpdatesMain.java) und es druckt '1.000.000 Einsätze dauerte 1.208 Sekunden' auf meinem Laptop. –

Antwort

14

Es gibt ein paar Punkte, die Sie vor dem Schreiben eines solchen Maßstab berücksichtigen müssen (und vor allem ein Benchmark mit der JVM):

  • auf den meisten (physischen) Maschinen ist Redis Lage, mehr als 100 K zu verarbeiten ops/s, wenn Pipelining verwendet wird. Ihr Benchmark behandelt nur 100K-Artikel und reicht daher nicht lange genug aus, um aussagekräftige Ergebnisse zu erzielen. Außerdem gibt es keine Zeit für die aufeinanderfolgenden Phasen des JIT, um in Gang zu kommen.

  • die absolute Zeit ist keine sehr relevante Metrik. Das Anzeigen des Durchsatzes (d. H. Die Anzahl der Operationen pro Sekunde), während der Benchmark mindestens 10 Sekunden lang läuft, wäre eine bessere und stabilere Metrik.

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

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

  • Optional können Sie 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 Bare-Bone-Interpreter und die Kosten für das JIT selbst zu vermeiden.

Jetzt, in Bezug auf Redis Pipelining, ist Ihre Pipeline viel zu lang. 100K Befehle in der Pipeline bedeutet, dass Jedis einen 6MB Puffer erstellen muss, bevor er etwas an Redis sendet. Das bedeutet, dass die Socket-Puffer (auf der Client-Seite und möglicherweise auf der Serverseite) gesättigt werden und dass Redis ebenfalls mit 6-MB-Kommunikationspuffern umgehen muss.

Darüber hinaus ist Ihr Benchmark immer noch synchron (eine Pipeline macht es nicht magisch asynchron). Mit anderen Worten, Jedis beginnt erst mit dem Lesen der Antworten, wenn die letzte Abfrage Ihrer Pipeline an Redis gesendet wurde. Wenn die Pipeline zu lang ist, hat sie das Potenzial, Dinge zu blockieren.

Sie können die Größe der Pipeline auf 100-1000 Operationen beschränken. Natürlich werden dadurch mehr Roundtrips generiert, aber der Druck auf den Kommunikationsstapel 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 das Pipelining verwendet wird, sodass es für mindestens 10 Sekunden ausgeführt wird. Wenn Sie den Ausdruck in der Schleife auskommentieren, werden Sie feststellen, dass das Programm am Anfang langsam ist und schneller wird, wenn das JIT beginnt, die Dinge zu optimieren (deshalb sollte das Programm mindestens einige Sekunden laufen, um ein aussagekräftiges Ergebnis zu liefern).

Auf meiner Hardware (eine alte Athlon-Box) kann ich 8-9 mal mehr Durchsatz bekommen, wenn die Pipeline benutzt 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.

+0

Können Sie mir sagen, wie Sie das Schlüssel/Wert-Format optimieren und eine Aufwärmphase hinzufügen können? – znlyj

+0

Formatierung: zum Beispiel, verwenden Sie Integer.toString (i) anstelle von "" + i –

+0

Warm-up-Phase: Fügen Sie nur ein paar Iterationen auf push (und stellen Sie sicher, obj.i gleich nach Null setzen) bevor startTime zugewiesen ist . –