2008-11-29 11 views
5

Tengo una página PHP particular que, por diversas razones, necesita guardar ~ 200 campos en una base de datos. Estas son 200 declaraciones separadas de inserción y/o actualización. Ahora lo más obvio es reducir este número pero, como dije, por razones que no me molestaré en abordar, no puedo hacer esto.¿Cómo implementar el almacenamiento en memoria caché de fondo/asíncrono en PHP?

No esperaba este problema. Las selecciones parecen tener un rendimiento razonable en MySQL, pero las inserciones/actualizaciones no lo son (toma aproximadamente 15-20 segundos realizar esta actualización, lo cual es naturalmente inaceptable). He escrito sistemas Java/Oracle que pueden hacer miles de inserciones/actualizaciones al mismo tiempo (en ambos casos ejecutando bases de datos locales, MySQL 5 vs OracleXE).

Ahora en algo como Java o .Net que podía hacer con bastante facilidad una de las siguientes:

  1. escribir los datos en una memoria en de escritura en segundo caché (es decir, que sería saber cómo persisten a la base de datos y podría hacerlo asincrónicamente);
  2. Escriba los datos en un caché en memoria y utilice el modelo PaaS (Persistencia como un Servicio) es decir, un detector de caché persistiría en los campos; o
  3. Simplemente inicie un proceso en segundo plano que podría persistir en los datos.

La solución mínima es tener un caché que simplemente puedo actualizar, que irá separadamente a la base de datos en su propio tiempo (es decir, volverá inmediatamente después de actualizar la caché en memoria). Esto puede ser un caché global o un caché de sesión (aunque un caché compartido global sí atrae de otras maneras).

¿Alguna otra solución a este tipo de problema?

Respuesta

1

Debería poder hacer 200 inserciones relativamente rápido, pero dependerá de muchos factores. Si usa un motor transaccional y hace cada uno en su propia transacción, no lo haga, eso crea demasiado E/S.

Si está utilizando un motor no transaccional, es un poco más complicado. Es probable que el uso de una sola inserción de varias filas sea mejor, ya que la política de descarga de MySQL significa que no necesitará eliminar sus cambios después de cada fila.

Realmente desea poder reproducir esto en su caja de desarrollo de especificaciones de producción y analizar exactamente por qué está sucediendo. No debería ser difícil parar.

Por supuesto, otra posibilidad es que las inserciones sean lentas debido a tablas de gran tamaño o gran cantidad de índices, en cuyo caso debe escalar el servidor de la base de datos de forma adecuada. Insertar muchas filas en una tabla cuyos índices no se ajustan a la RAM (o no tiene una RAM configurada correctamente para ser utilizada para el almacenamiento en caché de esos índices) generalmente se pone mal.

PERO no intente buscar una forma de complicar su aplicación cuando hay una forma de afinarla fácilmente, manteniendo el algoritmo actual.

+1

El bit de "optimización prematura" es un buen consejo. Las tablas no son demasiado complicadas, pero tengo un par de cosas que puedo verificar ahora (los índices y el motor de la base de datos). – cletus

+0

¿cómo responde la pregunta con respecto a la inserción asincrónica? – nightograph

1

Puede actualizar su caché local (con suerte memcached) y luego presionar las solicitudes de escritura a través del beanstalkd.

+0

Gracias por su respuesta. He echado un vistazo y hay muchas partes móviles: memcached, beanstalkd y supuestamente algo más para leer las colas de trabajo y persistir en los datos. – cletus

+0

Eso es correcto. Sin embargo, las partes son * realmente * simples y le permiten escalar su aplicación en gran medida. Te sentirás queriendo poner más cosas a través de los trabajadores (como enviar correos electrónicos, actualizar motores de búsqueda, etc.). memcached es ortogonal, pero debería estar en todas partes de todos modos. – Dustin

+0

¿Alguna sugerencia sobre qué usar para el proceso en segundo plano? Algo tendrá que leer desde la cola de trabajo y procesar las solicitudes. He visto algunos blogs sobre personas que usan Ruby con beanstalkd, posiblemente Java también. Aunque no estoy interesado en tener otro servidor en la mezcla. – cletus

0

Sospecho que hay un problema con sus inserciones SQL, realmente no debería tomar tanto tiempo. ¿Ayudarían las consultas preparadas? ¿Su servidor mysql necesita más memoria dedicada al espacio de claves? Creo que se necesitan más preguntas.

0

Cómo estás haciendo las inserciones, estás haciendo un inserto por registro

mysql_query('INSERT INTO tableName VALUES(...)'); 
mysql_query('INSERT INTO tableName VALUES(...)'); 
mysql_query('INSERT INTO tableName VALUES(...)'); 
mysql_query('INSERT INTO tableName VALUES(...)'); 
mysql_query('INSERT INTO tableName VALUES(...)'); 

o está usando una sola consulta

mysql_query('INSERT INTO tableName VALUES(...),(...),(...),(...)'); 

La última de las dos opciones es sustancialmente más rápido, y por experiencia, la primera opción hará que demore mucho más, ya que PHP debe esperar que termine la primera consulta antes de pasar al segundo y así sucesivamente.

+0

Cada actualización de campo es una actualización si ya existe o una inserción si no lo es. Ya sé si existe o no porque precargué todos los valores. Esto resolvería inserciones extensas pero las modificaciones son y serán principalmente actualizaciones. – cletus

+1

cletus puede usar INDICE ÚNICO en la base de datos y ACTUALIZAR CLAVE DUPLICADA en sus consultas para resolver ese problema utilizando esta solución. Google para ello :) –

1

Mire las estadísticas de su base de datos mientras hace las inserciones. Estoy adivinando que una de sus actualizaciones bloquea la tabla y por lo tanto todas sus declaraciones se ponen en cola y usted experimenta este retraso. Otra cosa a considerar es la creación/actualización de su índice porque cuantos más índices tenga en una tabla, más lentas serán las declaraciones UPDATE y INSERT.

Otra cosa es que creo que usas MYISAM (motor de tabla) que bloquea toda la tabla en ACTUALIZAR.Le sugiero que use INNODB en su lugar. INNODB es más lento en SELECT -cuenta, pero más rápido en INSERT y porque solo bloquea la fila en la que está trabajando y no toda la tabla.

+0

Una simplificación excesiva (y posiblemente inexacta) de las características de rendimiento de los motores de MySQL. El bloqueo de toda la mesa es probablemente más rápido que el bloqueo de filas (porque solo hay un bloqueo). – MarkR

+0

¿Qué? MYISAM bloquea toda la tabla, lo que hace que la escritura no esté disponible durante el bloqueo, lo que genera una gran acumulación (= retraso). Innodb solo bloquea la fila en la que está trabajando; otros procesos aún pueden escribir sobre la mesa. – Till

2

Una solución más que podría usar (en lugar de sintonizar mysql :)) es usar algún servidor JMS y STOMP connection driver para PHP para escribir datos en el servidor de bases de datos de manera asincrónica. ActiveMQ tienen soporte incorporado para el protocolo STOMP. Y hay un proyecto StompConnect que es un proxy STOMP para cualquier servidor compilado JMS (OpenMQ, JBossMQ, etc.).

+0

También OpenMQ, parte del servidor de aplicaciones GlassFish (pero también ejecutable sin él) es un servidor JMS que admitirá Stomp en la versión 4.4 – mjn

2

mysql_query ('INSERT INTO VALORES nombreTabla (...), (...), (...), (...)')

Por encima de instrucción de consulta dado es mejor. Pero tenemos otra solución para mejorar el rendimiento de la declaración de inserción. Siga los siguientes pasos: 1. Usted acaba de crear un csv (archivo delimitado por comas) o un simple archivo txt y escribe todos los datos que desea insertar usando la escritura de archivos machanism (como la clase FileOutputStream en java). 2. use este comando

CARGAR DATOS INFILE 'data.txt' EN LA TABLA table2 CAMPOS TERMINADOS POR '\ t';

3 si usted no está claro sobre este comando a continuación, siga el enlace a continuación

http://dev.mysql.com/doc/refman/5.1/en/load-data.html

0

considere esto:

mysql_query('start transaction'); 
mysql_query('INSERT INTO tableName VALUES(...)'); 
mysql_query('INSERT INTO tableName VALUES(...)'); 
mysql_query('INSERT INTO tableName VALUES(...)'); 
mysql_query('INSERT INTO tableName VALUES(...)'); 
mysql_query('INSERT INTO tableName VALUES(...)'); 
mysql_query('commit;') 
+0

¿Cómo hace esto la consulta en segundo plano? –

1

Tenga en cuenta que si la tabla es INSERT-solamente (sin eliminaciones, y no hay cambios en las columnas de longitud variable), a continuación, inserta va a bloquear o bloque lee cuando se utiliza MyISAM.

Esto puede mejorar o no el rendimiento de la inserción, pero podría ser útil si tiene problemas de inserción/lectura simultáneos.

Estoy usando esto, y solo purgando registros viejos a diario, seguidos por 'optimizar tabla'.

Cuestiones relacionadas