2012-02-22 9 views
8

Soy un novato en las bases de datos pero tengo un problema que parece que no puedo resolver. Lo siento de antemano si esto es demasiado largo, estoy tratando de resumir todos mis esfuerzos para que sepa exactamente lo que he hecho hasta ahora. Tengo una aplicación que tiene un poco de lógica y luego hace 3 consultas a una base de datos. La primera consulta verifica si existe un valor, segundo comprueba si existe otro valor (relacionado) y el tercero, si no existe, agrega un valor relacionado. Piense en mí haciendo una consulta sobre el número 2, y si existe, verifico 3 y lo agrego si es necesario. Hago este ciclo una gran cantidad de veces (estoy viendo consultas generales pero sospecho que este programa es más pesado que leído). Solía ​​usar solo una tabla hash en mi programa, pero como agregué varios procesos tuve problemas de sincronización, así que decidí usar una base de datos para que varios núcleos puedan trabajar en esto al mismo tiempo.¿Cómo puedo aumentar las consultas de lectura/segundo en mi base de datos?

Al principio probé, mysql y usé un motor de almacenamiento de memoria (todo cabía en la memoria), hice una clave primaria compuesta para replicar el diccionario que tenía en mi programa, lo indexé, deshabilité el bloqueo pero solo pude obtener alrededor de 11,000 consultas/segundo de eso.

Probé Redis (escuché que era como Memcache) y creé la misma clave/valor dict que tengo antes (aquí está el modo real Can I make two columns unique to each other? or use composite primary key's in redis?) y eliminé todas las cosas de fsync, así que espero que nunca llegue al disco duro I/O pero todavía solo obtiene alrededor de 30,000 consultas/segundo. Miré las mejoras del sistema (estoy usando Linux) haciendo que el programa se ejecute en un ramdrive, etc. pero el resultado es similar.

Tengo un script de configuración e intenté hacer esto en ec2 usando la instancia de cpu alta pero el resultado es similar (las consultas no aumentan mucho para ambas soluciones). Estoy algo desconcertado, pero no quiero rendirme porque leo sobre personas en stackoverflow hablando de cómo han obtenido 100,000k + consultas en una versión independiente. Siento que mi modelo de datos es muy simple (dos columnas de INT o I pueden hacer que sea una cadena con ambas INT combinadas, pero eso no parece disminuir la velocidad) y una vez que los datos son creados (y consultados por otro proceso) tengo no hay necesidad de persistencia (que es también la razón por la que intento no escribir en un disco duro). ¿Qué configuración me falta que permita a los desarrolladores obtener este tipo de rendimiento? ¿Se requiere alguna configuración especial fuera de la creación de la tabla? o es la única forma de obtener ese tipo de rendimiento a través de bases de datos distribuidas? Sé que el problema está en la base de datos porque cuando apago el proceso medio de la base de datos mi aplicación de Python llega al 100% en cada núcleo que se ejecuta (aunque no escribe nada), me hace pensar que el proceso de espera (para las lecturas, Sospecho) es lo que está desacelerando (tengo un montón de memoria/CPU libre, así que me pregunto por qué no está maximizando, tengo 50% de CPU y 80% de mi memoria libre durante estos trabajos, así que no tengo ni idea que esta pasando).

Tengo mysql, redis y hbase. Espero que haya algo que pueda hacer para que una de estas soluciones funcione tan rápido como me gustaría, pero si no lo hago, estoy de acuerdo con cualquier solución (en realidad es solo una tabla temporal que los procesos distribuidos pueden consultar).

¿Qué puedo hacer?

Gracias!

Actualización: como se solicita en los comentarios, aquí hay un código (después de la lógica de la aplicación específica que parece ir bien):

cursor.execute(""" SELECT value1 FROM data_table WHERE key1='%s' AND value1='%s' """ % (s - c * x, i)) 
    if cursor.rowcount == 1: 
     cursor.execute(""" SELECT value1 FROM data_table WHERE key1='%s' AND value1='%s' """ % (s, i+1)) 
     if cursor.rowcount == 0: 
      cursor.execute (""" INSERT INTO data_table (key1, value1) VALUES ('%s', '%s')""" % (s, i+1)) 
      conn.commit() #this maybe not needed 
      #print 'commited ', c 

anterior es el código con 3 búsquedas en MySQL.También he intentado hacer una gran consulta (pero era de hecho más lento):

 cursor.execute (""" 
INSERT INTO data_table (key1, value1) 
    SELECT '%s', '%s' 
    FROM dual 
    WHERE (SELECT COUNT(*) FROM data_table WHERE key1='%s' AND value1='%s') 
     = 1 
    AND NOT EXISTS 
     (SELECT * FROM data_table WHERE key1='%s' AND value1='%s') 
      """ % ((s), (i+1), (s - c * x), (i), (s), (i+1))) 

Aquí está el diseño de la tabla de MySQL:

cursor.execute ("DROP TABLE IF EXISTS data_table") 
cursor.execute (""" 
    CREATE TABLE data_table(
     key1 INT SIGNED NOT NULL, 
     value1 INT SIGNED NOT NULL, 
     PRIMARY KEY (key1,value1) 
    ) ENGINE=MEMORY 
""") 
cursor.execute("CREATE INDEX ValueIndex ON data_table (key1, value1)") 

en Redis, su simlair a la estructura 3 consulta (ya fue el más rápido que pude conseguir en MySQL, excepto que no necesito hacer una búsqueda si el valor existe, sólo sobreescribirla guardar una consulta):

if r_server.sismember(s - c * x, i): 
    r_server.sadd(s, i + 1) 

Mi estructura de datos para Redis es en el relacionado pregunta (básicamente es al ist, 3 => 1 2 3 en lugar de mysql que tiene 3 filas para volver a aplicar 3 = 1, 3 = 2, 3 = 3.

Espero que ayude, cualquier otra pregunta, por favor hágamelo saber.

+0

Es posible que pueda lograr esto con una consulta optimizada, guardando viajes redondos a la base de datos. Necesitamos más información, como las consultas, el esquema y la lógica. –

+0

@MarcusAdams Gracias por el comentario. Actualicé la pregunta con mi código mysql y redis. – Lostsoul

Respuesta

5

Al observar los fragmentos de código proporcionados, diría que el cuello de botella principal aquí son los rountrips de red o de bucle invertido TCP. Tanto MySQL como Redis son tiendas de cliente/servidor sincrónicas. Cada vez que envía una consulta y espera la respuesta, paga la programación del kernel, la latencia de la red, la proporción de aciertos de la memoria caché de la CPU, etc.

Las personas que ejecutan cientos de miles de consultas por segundo en TCP los servidores no usan un solo socket para apuntar al servidor, sino múltiples conexiones para el paralelismo del lado del cliente y/o pipeline their queries para limitar el impacto de esta latencia.

En realidad, si tiene un socket único y envía su consulta en secuencia sin ninguna canalización, no está midiendo el rendimiento máximo que puede lograr con un servidor, sino la latencia de la red o IPCs.

Afortunadamente, los protocolos utilizados por la mayoría de los servidores NoSQL generalmente admiten la canalización. Aquí hay algunos consejos para la implementación de Redis.

Es posible que desee leer the Redis benchmark page primero. Todos los cuellos de botella típicos de rendimiento que puede experimentar cuando se describen los puntos de referencia Redis.

Aquí hay algunos consejos para conseguir el máximo rendimiento para su referencia:

  • uso de un lenguaje eficiente (Python, Ruby, Javascript son mucho más lento que C)
  • en desarrollo de sus consultas en la medida de lo que pueda
  • si el cliente y el servidor están en la misma casilla, use los sockets de dominio de Unix en lugar de TCP loopback.
  • optimizar el sistema y en el nivel de red sólo después de que el software ha sido optimizado (configuración NUMA, NIC, etc ...)

He corrido una prueba simple usando hiredis (cliente C Redis) para simular su uso caso en un Xeon [email protected] El código se puede encontrar here.

if r_server.sismember(s - c * x, i): 
    r_server.sadd(s, i + 1) 

El programa implementa un código similar, canalizando las consultas. Encabeza los artículos y envía un conjunto de comandos sismember para saber si los artículos existen o no, y luego un montón de comandos sadd para los elementos que tiene que agregar.

Resultados:

  • Sin canalización, con bucle invertido TCP => 66268 q/s
  • Sin canalización, con sockets de dominio Unix => 89485 q/s
  • Con la canalización y de bucle invertido TCP => 273 757 q/s
  • Con la canalización y zócalos de dominio UNIX => 278 254 q/s

Por lo tanto, el impacto del uso de conectores de dominio Unix es alto cuando los recorridos de ida y vuelta no están optimizados, y se vuelve muy bajo una vez que se utiliza la canalización. La mayor parte de la ganancia se debe a la canalización. Es por eso que debe centrarse primero en optimizar el software/protocolo.

Los resultados pueden mejorarse ajustando la configuración del sistema/red, pero el siguiente paso para obtener más rendimiento normalmente es ejecutar varias instancias de Redis y fragmentar los datos utilizando un mecanismo hash (tratando de paralelizar en el servidor) .

+0

Wow Didier, muchas gracias. Tu respuesta me dio muchas ideas y estoy ansioso por ver tu código. Sus resultados son lo suficientemente impresionantes como para hacerme comprender el poder de la canalización. Investigué antes y pensé que el redis no podía ejecutarse en un entorno distribuido y, por lo tanto, no podía copiarse en otros servidores redis. Si es posible, ¿qué debería investigar para encontrarlo? – Lostsoul

+0

También noté que lo hizo en un xeón ... ¿hay alguna manera de que el redis use más de un núcleo (en mi configuración parece que solo usa uno)? Tu código puede estar bifurcando, así que lo verificaré. – Lostsoul

+0

Es ciertamente posible utilizar varias instancias de Redis en un entorno distribuido; debe diseñar su estrategia de fragmentación e implementarla en el lado del cliente. Para ver un buen ejemplo, consulte http://blog.zawodny.com/2011/02/26/redis-sharding-at-craigslist/ –

Cuestiones relacionadas