2010-05-24 42 views
10

Tengo una base de datos utilizada para almacenar elementos y propiedades sobre estos elementos. El número de propiedades es extensible, por lo tanto, hay una tabla de unión para almacenar cada propiedad asociada a un valor de elemento.¿Cómo manejar una tabla grande en MySQL?

CREATE TABLE `item_property` (
    `property_id` int(11) NOT NULL, 
    `item_id` int(11) NOT NULL, 
    `value` double NOT NULL, 
    PRIMARY KEY (`property_id`,`item_id`), 
    KEY `item_id` (`item_id`) 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; 

Esta base de datos tiene dos objetivos: el almacenamiento (que tiene la primera prioridad y tiene que ser muy rápido, me gustaría realizar muchas inserciones (cientos) en pocos segundos), la recuperación de datos (selecciona utilizando item_id y property_id) (Esta es una segunda prioridad, puede ser más lenta pero no demasiado porque esto arruinaría mi uso de la base de datos).

Actualmente, esta tabla alberga 1,6 billones de entradas y una cuenta simple puede llevar hasta 2 minutos ... La inserción no es lo suficientemente rápida como para ser utilizable.

Estoy usando Zend_Db para acceder a mis datos y realmente me alegraría si no me sugiera que desarrolle cualquier elemento secundario de PHP.

+0

la pregunta no está realmente relacionada con php, así que eliminé esta etiqueta – jigfox

+0

No hay problema Jens, usted es correcto – AsTeR

Respuesta

10

Si no puede buscar soluciones usando diferentes sistemas de administración de bases de datos o particiones en un clúster por alguna razón, todavía hay tres cosas principales que puede hacer para radicalmente mejorar su rendimiento (y funcionan en combinación con clusters también, por supuesto):

  • de configuración del motor MyISAM de almacenamiento
  • uso "LOAD DATA INFILE nombre de fichero EN TABLA nombretabla"
  • dividir sus datos a través de varias mesas

Eso es todo. Lea el resto solo si está interesado en los detalles :)

¿Sigue leyendo? OK, entonces, aquí va: MyISAM es la piedra angular, ya que es el motor más rápido de lejos. En lugar de insertar filas de datos con sentencias de SQL comunes, debe mezclarlas en un archivo y insert that file a intervalos regulares (tan a menudo como lo necesite, pero tan pocas veces como lo permita su aplicación sería lo mejor). De esta forma, puede insertar en el orden de un millón de filas por minuto.

Lo siguiente que lo limitará son sus claves/índices. Cuando esos no pueden caber en su memoria (porque son simplemente grandes) experimentará una gran desaceleración tanto en las inserciones como en las consultas. Es por eso que divide los datos en varias tablas, todas con el mismo esquema. Cada mesa debe ser lo más grande posible, sin llenar su memoria cuando se cargan de a una por vez. El tamaño exacto depende de su máquina e índices, por supuesto, pero debe estar entre 5 y 50 millones de filas/tabla. Encontrará esto si simplemente mide el tiempo necesario para insertar un montón de filas detrás de otro, buscando el momento en que se ralentiza significativamente. Cuando conozca el límite, cree una nueva tabla sobre la marcha cada vez que su última tabla se acerque a ese límite.

La consecuencia de la solución multitarea es que tendrás que consultar todas tus tablas en lugar de solo una cuando necesites algunos datos, lo que ralentizará un poco tus consultas (pero no demasiado si lo haces " solo "tiene un billón o más filas"). Obviamente, hay optimizaciones para hacer aquí también. Si hay algo fundamental que podría utilizar para separar los datos (como la fecha, el cliente o algo así), podría dividirlo en diferentes tablas utilizando un patrón estructurado que le permita saber dónde están determinados tipos de datos, incluso sin consultar las tablas. Utilice ese conocimiento para consultar solo las tablas que puedan contener los datos solicitados, etc.

Si necesita aún más sintonización, vaya a partitioning, como lo sugieren Eineki y oedo.

Además, por lo que sabrá que todo esto no es una especulación descabellada: estoy haciendo algunas pruebas de escalabilidad como esta en nuestros propios datos en este momento y este enfoque nos está haciendo maravillas. Estamos logrando insertar decenas de millones de filas todos los días y las consultas toman ~ 100 ms.

+0

¡Rock'n'roll parece ser el más completo!No probaré el "infile de datos de carga", no tengo ninguna voluntad de reescribir el código en el lado de PHP, y eso me obligaría a hacerlo. Voy a probar las cosas de partición y también el motor cambiará a MyISAM. – AsTeR

+0

La actualización de 5.0 a 5.1 me ofrece una primera mejora de rendimiento. Primero eliminé todas las claves foráneas y usé 20 particiones. Una selección simple para obtener todas las propiedades (prueba 1): va de 0,7 segundos a 0,37. Un recuento de todos los elementos (prueba 2) va de más de un minuto a 11 segundos. Entonces testest 200 particiones: de prueba 1: 0,29 s prueba 2: 14,86 s Finalmente utilicé 50 paritions, cambiado a myisam y quitado el índice: prueba 1: 0,24 s prueba 2: <0,01 s ¡Gracias a todos! – AsTeR

0

En primer lugar, no use InnoDb ya que no parece necesitar su característica principal sobre MyISAM (bloqueo, transacción, etc.). Así que use MyISAM, ya hará una diferencia. Entonces, si eso aún no es lo suficientemente rápido, ingrese en una indexación, pero ya debería ver una diferencia radical.

+1

MyISAM bien puede ser * peor * que InnoDB incluso puramente a la velocidad. Si esas actualizaciones entran simultáneamente, es probable que el bloqueo a nivel de tabla de MyISAM tenga un fuerte efecto negativo. – bobince

0

wow, que es bastante una gran mesa :)

si necesita almacenar para ser rápido, usted podría lote de seguridad de sus inserciones e insertarlos con una sola instrucción INSERT múltiple. sin embargo, esto definitivamente requeriría un código adicional del lado del cliente (php), ¡lo siento!

INSERT INTO `table` (`col1`, `col2`) VALUES (1, 2), (3, 4), (5, 6)... 

también deshabilitan los índices que NECESITAN ya que los índices ralentizan los comandos de inserción.

, alternativamente, usted podría mirar a la partición de la tabla: linky

+0

La idea es agradable, pero estoy disfrutando mucho de Zend_Db para probar esto. – AsTeR

0

Mira en Memcache para ver donde se puede aplicar. También busque en la división horizontal para mantener los tamaños/índices de tabla más pequeños.

+0

Ya he usado Memcache ... no se ajusta a mis necesidades. No tengo nada que guardar en caché. Almazo datos en períodos largos y luego los recupero preprocesados. – AsTeR

0

Primero: una mesa con 1,6 mil millones de entradas parece ser demasiado grande. Trabajo en algunos sistemas de carga bastante pesados, donde incluso las tablas de registro que realizan un seguimiento de todas las acciones no se vuelven tan grandes a lo largo de los años. Entonces, si es posible, piense si puede encontrar un método de almacenamiento más óptimo. No puedo dar muchos más consejos ya que no conozco su estructura de base de datos, pero estoy seguro de que habrá mucho espacio para la optimización. 1,6 mil millones de entradas es demasiado grande.

Un par de cosas sobre el rendimiento:

Si usted no necesita comprobaciones de integridad referencial, lo cual es poco probable, que podrían cambiar al motor de almacenamiento MyISAM. Es un poco más rápido pero carece de transacciones y ckecks de integridad.

Para cualquier otra cosa, más información sería necesaria.

+0

Al igual que algunos otros dicen aquí, he leído que MyISAM no lo hará más rápido, pero lo intentaré. – AsTeR

+0

Por cierto, no estoy usando ninguna característica de innoDB – AsTeR

0

¿Ha considerado la opción de partitioning la tabla?

+0

No, no lo he hecho y creo que podría ser un punto de optimización serio. – AsTeR

-1

Una cosa importante para recordar es que una instalación predeterminada de MySQL no está configurada para trabajos pesados ​​como este. Asegúrese de tener tuned it para su carga de trabajo.

Cuestiones relacionadas