sql >> Base de Datos >  >> NoSQL >> Redis

¿Por qué es tan lento con 100 000 registros cuando se usa la canalización en redis?

Hay algunos puntos que debe tener en cuenta antes de escribir un punto de referencia de este tipo (y especialmente un punto de referencia que utilice la JVM):

  • en la mayoría de las máquinas (físicas), Redis puede procesar más de 100 000 operaciones/s cuando se usa canalización. Su punto de referencia solo se ocupa de 100 000 elementos, por lo que no dura lo suficiente como para producir resultados significativos. Además, no hay tiempo para que se activen las sucesivas etapas del JIT.

  • el tiempo absoluto no es una métrica muy relevante. Mostrar el rendimiento (es decir, el número de operaciones por segundo) mientras se mantiene el punto de referencia funcionando durante al menos 10 segundos sería una métrica mejor y más estable.

  • tu bucle interno genera mucha basura. Si planea comparar Jedis+Redis, debe mantener bajos los gastos generales de su propio programa.

  • debido a que ha definido todo en la función principal, su ciclo no será compilado por el JIT (dependiendo de la JVM que use). Solo las llamadas a métodos internos pueden serlo. Si desea que el JIT sea eficiente, asegúrese de encapsular su código en métodos que el JIT pueda compilar.

  • Opcionalmente, es posible que desee agregar una fase de calentamiento antes de realizar la medición real para evitar tener en cuenta la sobrecarga de ejecutar las primeras iteraciones con el intérprete básico y el costo del propio JIT.

Ahora, con respecto a la canalización de Redis, su canalización es demasiado larga. 100 000 comandos en proceso significa que Jedis tiene que crear un búfer de 6 MB antes de enviar algo a Redis. Significa que los búferes de socket (en el lado del cliente y quizás en el lado del servidor) se saturarán y que Redis también tendrá que lidiar con búferes de comunicación de 6 MB.

Además, su punto de referencia sigue siendo síncrono (el uso de una canalización no lo convierte mágicamente en asíncrono). En otras palabras, Jedis no comenzará a leer las respuestas hasta que la última consulta de su canalización se haya enviado a Redis. Cuando la canalización es demasiado larga, tiene el potencial de bloquear cosas.

Considere limitar el tamaño de la tubería a 100-1000 operaciones. Por supuesto, generará más viajes de ida y vuelta, pero la presión sobre la pila de comunicación se reducirá a un nivel aceptable. Por ejemplo, considere el siguiente programa:

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 este programa, puede probar con o sin segmentación. Asegúrese de aumentar el número de iteraciones (parámetro N) cuando se utiliza la canalización, de modo que se ejecute durante al menos 10 segundos. Si quita el comentario de println en el ciclo, se dará cuenta de que el programa es lento al principio y se volverá más rápido a medida que el JIT comience a optimizar las cosas (es por eso que el programa debe ejecutarse al menos varios segundos para dar un resultado significativo).

En mi hardware (una vieja caja Athlon), puedo obtener entre 8 y 9 veces más rendimiento cuando se usa la canalización. El programa podría mejorarse aún más optimizando el formato de clave/valor en el ciclo interno y agregando una fase de calentamiento.