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

redis + gevent - Bajo rendimiento - ¿Qué estoy haciendo mal?

Esto es de esperar.

Ejecuta este punto de referencia en una máquina virtual, en la que el costo de las llamadas al sistema es más alto que en el hardware físico. Cuando gevent está activado, tiende a generar más llamadas al sistema (para manejar el dispositivo epoll), por lo que termina con menos rendimiento.

Puede verificar fácilmente este punto usando strace en el script.

Sin gevent, el ciclo interno genera:

recvfrom(3, ":931\r\n", 4096, 0, NULL, NULL) = 6
sendto(3, "*3\r\n$6\r\nINCRBY\r\n$10\r\ntestsocket\r"..., 41, 0, NULL, 0) = 41
recvfrom(3, ":941\r\n", 4096, 0, NULL, NULL) = 6
sendto(3, "*3\r\n$6\r\nINCRBY\r\n$10\r\ntestsocket\r"..., 41, 0, NULL, 0) = 41

Con gevent, tendrá ocurrencias de:

recvfrom(3, ":221\r\n", 4096, 0, NULL, NULL) = 6
sendto(3, "*3\r\n$6\r\nINCRBY\r\n$10\r\ntestsocket\r"..., 41, 0, NULL, 0) = 41
recvfrom(3, 0x7b0f04, 4096, 0, 0, 0)    = -1 EAGAIN (Resource temporarily unavailable)
epoll_ctl(5, EPOLL_CTL_ADD, 3, {EPOLLIN, {u32=3, u64=3}}) = 0
epoll_wait(5, {{EPOLLIN, {u32=3, u64=3}}}, 32, 4294967295) = 1
clock_gettime(CLOCK_MONOTONIC, {2469, 779710323}) = 0
epoll_ctl(5, EPOLL_CTL_DEL, 3, {EPOLLIN, {u32=3, u64=3}}) = 0
recvfrom(3, ":231\r\n", 4096, 0, NULL, NULL) = 6
sendto(3, "*3\r\n$6\r\nINCRBY\r\n$10\r\ntestsocket\r"..., 41, 0, NULL, 0) = 41

Cuando la llamada recvfrom se bloquea (EAGAIN), gevent vuelve al bucle de eventos, por lo que se realizan llamadas adicionales para esperar los eventos de los descriptores de archivos (epoll_wait).

Tenga en cuenta que este tipo de punto de referencia es el peor de los casos para cualquier sistema de bucle de eventos, porque solo tiene un descriptor de archivo, por lo que las operaciones de espera no se pueden factorizar en varios descriptores. Además, las E/S asíncronas no pueden mejorar nada aquí ya que todo es síncrono.

También es el peor de los casos para Redis porque:

  • genera muchos viajes de ida y vuelta al servidor

  • se conecta/desconecta sistemáticamente (1000 veces) porque el grupo se declara en la función UxDomainSocket.

En realidad, su punto de referencia no prueba gevent, redis o redis-py:ejerce la capacidad de una VM para sostener un juego de ping-pong entre 2 procesos.

Si desea aumentar el rendimiento, debe:

  • use canalización para disminuir el número de viajes de ida y vuelta

  • hacer que el grupo sea persistente en todo el punto de referencia

Por ejemplo, considere con el siguiente script:

#!/usr/bin/python

from gevent import monkey
monkey.patch_all()

import timeit
import redis
from redis.connection import UnixDomainSocketConnection

pool = redis.ConnectionPool(connection_class=UnixDomainSocketConnection, path = '/tmp/redis.sock')

def UxDomainSocket():
    r = redis.Redis(connection_pool = pool)
    p = r.pipeline(transaction=False)
    p.set("testsocket", 1)
    for i in range(100):
        p.incr('testsocket', 10)
    p.get('testsocket')
    p.delete('testsocket')
    p.execute()

print timeit.Timer(stmt='UxDomainSocket()', setup='from __main__ import UxDomainSocket').timeit(number=1000)

Con este script, obtengo un rendimiento 3 veces mejor y casi no tengo gastos generales con gevent.