Esta es mi consulta lenta:
SELECT `products_counts`.`cid`
FROM
`products_counts` `products_counts`
LEFT OUTER JOIN `products` `products` ON (
`products_counts`.`product_id` = `products`.`id`
)
LEFT OUTER JOIN `trademarks` `trademark` ON (
`products`.`trademark_id` = `trademark`.`id`
)
LEFT OUTER JOIN `suppliers` `supplier` ON (
`products_counts`.`supplier_id` = `supplier`.`id`
)
WHERE
`products_counts`.product_id IN
(159, 572, 1075, 1102, 1145, 1162, 1660, 2355, 2356, 2357, 3236, 6471, 6472, 6473, 8779, 9043, 9095, 9336, 9337, 9338, 9445, 10198, 10966, 10967, 10974, 11124, 11168, 16387, 16689, 16827, 17689, 17920, 17938, 17946, 17957, 21341, 21352, 21420, 21421, 21429, 21544, 27944, 27988, 30194, 30196, 30230, 30278, 30699, 31306, 31340, 32625, 34021, 34047, 38043, 43743, 48639, 48720, 52453, 55667, 56847, 57478, 58034, 61477, 62301, 65983, 66013, 66181, 66197, 66204, 66407, 66844, 66879, 67308, 68637, 73944, 74037, 74060, 77502, 90963, 101630, 101900, 101977, 101985, 101987, 105906, 108112, 123839, 126316, 135156, 135184, 138903, 142755, 143046, 143193, 143247, 144054, 150164, 150406, 154001, 154546, 157998, 159896, 161695, 163367, 170173, 172257, 172732, 173581, 174001, 175126, 181900, 182168, 182342, 182858, 182976, 183706, 183902, 183936, 184939, 185744, 287831, 362832, 363923, 7083107, 7173092, 7342593, 7342594, 7342595, 7728766)
ORDER BY
products_counts.inflow ASC,
supplier.delivery_period ASC,
trademark.sort DESC,
trademark.name ASC
LIMIT
0, 3;
El tiempo promedio de consulta es de 4.5s en mi conjunto de datos y esto es inaceptable.
Soluciones que veo:
Agregue todas las columnas de la cláusula order a la products_counts
tabla. Pero tengo ~ 10 tipos de orden en la aplicación, por lo que debería crear muchas columnas e índices. Además, products_counts
tengo actualizaciones / inserciones / eliminaciones muy intensas, por lo que necesito realizar una actualización inmediata de todas las columnas relacionadas con el orden (¿usando disparadores?).
¿Hay otra solución?
Explique:
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
| 1 | SIMPLE | products_counts | range | product_id_supplier_id,product_id,pid_count | product_id_supplier_id | 4 | NULL | 227 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | products | eq_ref | PRIMARY | PRIMARY | 4 | uaot.products_counts.product_id | 1 | |
| 1 | SIMPLE | trademark | eq_ref | PRIMARY | PRIMARY | 4 | uaot.products.trademark_id | 1 | |
| 1 | SIMPLE | supplier | eq_ref | PRIMARY | PRIMARY | 4 | uaot.products_counts.supplier_id | 1 | |
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
Estructura de tablas:
CREATE TABLE `products_counts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`product_id` int(11) unsigned NOT NULL,
`supplier_id` int(11) unsigned NOT NULL,
`count` int(11) unsigned NOT NULL,
`cid` varchar(64) NOT NULL,
`inflow` varchar(10) NOT NULL,
`for_delete` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `cid` (`cid`),
UNIQUE KEY `product_id_supplier_id` (`product_id`,`supplier_id`),
KEY `product_id` (`product_id`),
KEY `count` (`count`),
KEY `pid_count` (`product_id`,`count`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `products` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`external_id` varchar(36) NOT NULL,
`name` varchar(255) NOT NULL,
`category_id` int(11) unsigned NOT NULL,
`trademark_id` int(11) unsigned NOT NULL,
`photo` varchar(255) NOT NULL,
`sort` int(11) unsigned NOT NULL,
`otech` tinyint(1) unsigned NOT NULL,
`not_liquid` tinyint(1) unsigned NOT NULL DEFAULT '0',
`applicable` varchar(255) NOT NULL,
`code_main` varchar(64) NOT NULL,
`code_searchable` varchar(128) NOT NULL,
`total` int(11) unsigned NOT NULL,
`slider` int(11) unsigned NOT NULL,
`slider_title` varchar(255) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `external_id` (`external_id`),
KEY `category_id` (`category_id`),
KEY `trademark_id` (`trademark_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `trademarks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`external_id` varchar(36) NOT NULL,
`name` varchar(255) NOT NULL,
`country_id` int(11) NOT NULL,
`sort` int(11) unsigned NOT NULL DEFAULT '0',
`sort_list` int(10) unsigned NOT NULL DEFAULT '0',
`is_featured` tinyint(1) unsigned NOT NULL,
`is_direct` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `external_id` (`external_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `suppliers` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`external_id` varchar(36) NOT NULL,
`code` varchar(64) NOT NULL,
`name` varchar(255) NOT NULL,
`delivery_period` tinyint(1) unsigned NOT NULL,
`is_default` tinyint(1) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `external_id` (`external_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Información del servidor MySQL:
mysqld Ver 5.5.45-1+deb.sury.org~trusty+1 for debian-linux-gnu on i686 ((Ubuntu))
(inflow, product_id)
?innodb_buffer_pool_size
. Por lo general, alrededor del 70% de la RAM disponible es buena.Respuestas:
La revisión de las definiciones de la tabla muestra que tiene índices coincidentes entre las tablas involucradas. Esto debería hacer que las uniones sucedan lo más rápido posible dentro de los límites de la
MySQL's
lógica de unión.Sin embargo, ordenar de varias tablas es más complejo.
En 2007, Sergey Petrunia describió los 3
MySQL
algoritmos de clasificación por orden de velocidadMySQL
en: http://s.petrunia.net/blog/?m=201407filesort()
en la primera tabla no constantefilesort()
en ellaDe las definiciones de tabla y combinaciones que se muestran arriba, puede ver que nunca obtendrá la clasificación más rápida . Eso significa que dependerá
filesort()
de los criterios de clasificación que esté utilizando.Sin embargo, si diseña y usa una Vista Materializada , podrá usar el algoritmo de ordenación más rápido.
Para
MySQL 5.5
(en este ejemplo) aumentar laORDER BY
velocidad si no puedeMySQL
usar índices en lugar de una fase de clasificación adicional, pruebe las siguientes estrategias:• Aumentar el
sort_buffer_size
valor de la variable.• Aumentar el
read_rnd_buffer_size
valor de la variable.• Use menos RAM por fila declarando columnas tan grandes como sea necesario para que se almacenen los valores reales. [Por ejemplo, reducir un varchar (256) a varchar (ActualLongestString)]
• Cambie la
tmpdir
variable del sistema para que apunte a un sistema de archivos dedicado con grandes cantidades de espacio libre. (Otros detalles se ofrecen en el enlace de arriba).Vistas materializadas : un enfoque diferente para clasificar tablas unidas
Aludió a Vistas materializadas con su pregunta que se refiere al uso de disparadores. MySQL no tiene una funcionalidad integrada para crear una vista materializada, pero sí tiene las herramientas necesarias. Al utilizar disparadores para distribuir la carga, puede mantener la Vista materializada hasta el momento.
La Vista materializada es en realidad una tabla que se rellena a través de un código de procedimiento para construir o reconstruir la Vista materializada y que se mantiene mediante disparadores para mantener los datos actualizados.
Como está creando una tabla que tendrá un índice , la Vista materializada cuando se le consulte puede usar el método de clasificación más rápido : use un método de acceso basado en índice que produzca resultados ordenados
Dado que
MySQL 5.5
utiliza disparadores para mantener una Vista materializada , también necesitará un proceso, secuencia de comandos o procedimiento almacenado para construir la Vista materializada inicial .Pero ese es obviamente un proceso demasiado pesado para ejecutarse después de cada actualización de las tablas base donde administra los datos. Ahí es donde los disparadores entran en juego para mantener los datos actualizados a medida que se realizan los cambios. De esta manera
insert
, cada ,update
ydelete
propagará sus cambios, usando sus disparadores, a la Vista Materializada .La organización FROMDUAL en http://www.fromdual.com/ tiene un código de muestra para mantener una Vista materializada . Entonces, en lugar de escribir mis propias muestras, te señalaré sus muestras:
http://www.fromdual.com/mysql-materialized-views
Ejemplo 1: crear una vista materializada
Esto le brinda la Vista materializada en el momento de la actualización. Sin embargo, dado que tiene una base de datos que se mueve rápidamente, también desea mantener esta vista lo más actualizada posible.
Por lo tanto, las tablas de datos base afectadas deben tener activadores para propagar los cambios de una tabla base a la tabla Vista materializada . Como un ejemplo:
Ejemplo 2: Insertar datos nuevos en una vista materializada
Por supuesto, también necesitará disparadores para mantener Eliminar datos de una vista materializada y actualizar datos en una vista materializada . Las muestras también están disponibles para estos desencadenantes.
AL FIN: ¿Cómo hace que la clasificación de tablas unidas sea más rápida?
La Vista materializada se construye constantemente a medida que se le realizan actualizaciones. Por lo tanto, puede definir el índice (o índices ) que desea usar para ordenar los datos en la vista o tabla materializada .
Si la sobrecarga de mantener los datos no es demasiado pesada, entonces está gastando algunos recursos (CPU / IO / etc.) para cada cambio de datos relevante para mantener la Vista materializada y, por lo tanto, los datos del índice están actualizados y fácilmente disponibles. Por lo tanto, la selección será más rápida, ya que usted:
Dependiendo de sus circunstancias y de cómo se sienta sobre el proceso general, es posible que desee reconstruir las Vistas materializadas todas las noches durante un período lento.
fuente
No hay mucho que hacer aquí, pero supongo que el problema principal es que está creando una tabla temporal bastante grande y ordena el archivo en el disco cada vez. La razón es:
Esto significa que su tabla temporal y el archivo de clasificación podrían ser bastante grandes, ya que al crear la tabla temporal los campos se crean en la longitud MÁXIMA, y al ordenar los registros están todos en la longitud MÁXIMA (y UTF8 es de 3 bytes por carácter). También es probable que esto impida el uso de una tabla temporal en memoria. Para obtener más información, consulte los detalles de las tablas temporales internas .
El LÍMITE tampoco nos sirve de nada aquí, ya que necesitamos materializar y ordenar todo el conjunto de resultados antes de saber cuáles son las primeras 3 filas.
¿Has intentado mover tu tmpdir a un sistema de archivos tmpfs ? Si / tmp aún no está usando tmpfs (MySQL usa
tmpdir=/tmp
de forma predeterminada en * nix), entonces podría usar / dev / shm directamente. En su archivo my.cnf:Entonces necesitarías reiniciar mysqld.
Eso podría hacer una gran diferencia. Si es probable que se vea sometido a una presión de memoria en el sistema, es probable que desee limitar el tamaño (por lo general, las distribuciones de Linux cap tmpfs al 50% de la RAM total de forma predeterminada) para evitar intercambiar segmentos de memoria en el disco, o incluso peor una situación OOM . Puede hacerlo editando la línea en
/etc/fstab
:También puede cambiar su tamaño "en línea". Por ejemplo:
También puede actualizar a MySQL 5.6, que tiene subconsultas de rendimiento y tablas derivadas, y jugar un poco más con la consulta. Sin embargo, no creo que veamos grandes victorias en esa ruta, por lo que veo.
¡Buena suerte!
fuente