Voici ma lente requête:
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;
Le temps de requête moyen est de 4,5 secondes sur mon jeu de données, ce qui est inacceptable.
Solutions que je vois:
Ajoutez toutes les colonnes de la clause order à la products_counts
table. Mais j'ai ~ 10 types de commandes en application, donc je devrais créer beaucoup de colonnes et d'index. De products_counts
plus, les mises à jour / insertions / suppressions sont très intensives, j'ai donc besoin de mettre à jour immédiatement toutes les colonnes liées à la commande (à l'aide de déclencheurs?).
Y a-t-il une autre solution?
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 | |
+----+-------------+-----------------+--------+---------------------------------------------+------------------------+---------+----------------------------------+------+----------------------------------------------+
Structure des tables:
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;
Informations sur le serveur 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
. En général, environ 70% de la RAM disponible est bonne.Réponses:
L'examen de vos définitions de table montre que vous avez des index correspondant à travers les tables impliquées. Cela devrait permettre aux jointures de se produire le plus rapidement possible dans les limites de la
MySQL's
logique de jointure.Cependant, le tri à partir de plusieurs tables est plus complexe.
En 2007, Sergey Petrunia a décrit les 3
MySQL
algorithmes de tri par ordre de vitesse pourMySQL
à: http://s.petrunia.net/blog/?m=201407filesort()
sur la 1ère table non constantefilesort()
leÀ partir des définitions de table et des jointures ci-dessus, vous pouvez voir que vous n'obtiendrez jamais le tri le plus rapide . Cela signifie que vous serez tributaire
filesort()
des critères de tri que vous utilisez.Cependant, si vous concevez et utilisez une vue matérialisée, vous pourrez utiliser l' algorithme de tri le plus rapide.
Pour
MySQL 5.5
(dans cet exemple) augmenter laORDER BY
vitesse si vous ne pouvez pasMySQL
utiliser les index plutôt qu'une phase de tri supplémentaire, essayez les stratégies suivantes:• Augmentez la
sort_buffer_size
valeur de la variable.• Augmentez la
read_rnd_buffer_size
valeur de la variable.• Utilisez moins de RAM par ligne en déclarant les colonnes uniquement aussi grandes que nécessaire pour les valeurs réelles à stocker. [Par exemple, réduire un varchar (256) en varchar (ActualLongestString)]
• Modifiez la
tmpdir
variable système pour pointer vers un système de fichiers dédié avec de grandes quantités d'espace libre. (D'autres détails sont proposés dans le lien ci-dessus.)Vues matérialisées - Une approche différente pour trier les tables jointes
Vous avez fait allusion aux vues matérialisées avec votre question concernant l'utilisation des déclencheurs. MySQL n'a pas de fonctionnalité intégrée pour créer une vue matérialisée, mais vous disposez des outils nécessaires. En utilisant des déclencheurs pour répartir la charge, vous pouvez maintenir la vue matérialisée jusqu'à présent.
La vue matérialisée est en fait une table qui est remplie à travers du code procédural pour construire ou reconstruire la vue matérialisée et maintenue par des déclencheurs pour garder les données à jour.
Puisque vous créez une table qui aura un index , la vue matérialisée interrogée peut utiliser la méthode de tri la plus rapide : utilisez une méthode d'accès basée sur un index qui produit une sortie ordonnée
Comme
MySQL 5.5
utilise des déclencheurs pour maintenir une vue matérialisée , vous aurez également besoin d'un processus, d'un script ou d'une procédure stockée pour créer la vue matérialisée initiale .Mais c'est évidemment un processus trop lourd à exécuter après chaque mise à jour des tables de base où vous gérez les données. C'est là que les déclencheurs entrent en jeu pour maintenir les données à jour lorsque des modifications sont apportées. De cette façon , chacun
insert
,update
etdelete
se propager leurs changements, en utilisant vos déclencheurs, à la vue matérialisée .L'organisation FROMDUAL sur http://www.fromdual.com/ a un exemple de code pour maintenir une vue matérialisée . Donc, plutôt que d'écrire mes propres échantillons, je vous indiquerai leurs échantillons:
http://www.fromdual.com/mysql-materialized-views
Exemple 1: création d'une vue matérialisée
Cela vous donne la vue matérialisée au moment de l'actualisation. Cependant, étant donné que vous disposez d'une base de données évoluant rapidement, vous souhaitez également conserver cette vue aussi à jour que possible.
Par conséquent, les tables de données de base affectées doivent avoir des déclencheurs pour propager les modifications d'une table de base vers la table Vue matérialisée . À titre d'exemple:
Exemple 2: insertion de nouvelles données dans une vue matérialisée
Bien sûr, vous aurez également besoin de déclencheurs pour maintenir la suppression des données d'une vue matérialisée et la mise à jour des données dans une vue matérialisée . Des échantillons sont également disponibles pour ces déclencheurs.
ENFIN: Comment cela rend-il le tri des tables jointes plus rapide?
La vue matérialisée est constamment construite au fur et à mesure des mises à jour. Par conséquent, vous pouvez définir l' index (ou les index ) que vous souhaitez utiliser pour trier les données dans la vue matérialisée ou la table .
Si les frais généraux liés à la maintenance des données ne sont pas trop lourds, vous dépensez des ressources (CPU / IO / etc) pour chaque modification de données pertinente pour conserver la vue matérialisée et, par conséquent, les données d'index sont à jour et facilement disponibles. Par conséquent, la sélection sera plus rapide, car vous:
En fonction de votre situation et de ce que vous pensez du processus global, vous souhaiterez peut-être reconstruire les vues matérialisées chaque nuit pendant une période lente.
la source
Il n'y a pas grand-chose à faire ici, mais je suppose que le principal problème est que vous créez à chaque fois une table temporaire et un fichier de tri assez volumineux sur le disque. La raison étant:
Cela signifie que votre table temporaire et votre fichier de tri peuvent être assez volumineux, car lors de la création de la table temporaire, les champs sont créés à la longueur MAX et lorsque le tri des enregistrements est à la longueur MAX (et UTF8 est de 3 octets par caractère). Celles-ci empêchent également probablement l'utilisation d'une table temporaire en mémoire. Pour plus d'informations, voir les détails des tables temporaires internes .
La LIMIT ne nous sert également à rien ici, car nous devons matérialiser et ordonner l'ensemble des résultats avant de savoir quelles sont les 3 premières lignes.
Avez-vous essayé de déplacer votre tmpdir vers un système de fichiers tmpfs ? Si / tmp n'utilise pas déjà tmpfs (MySQL utilise
tmpdir=/tmp
par défaut sur * nix), vous pouvez utiliser directement / dev / shm. Dans votre fichier my.cnf:Ensuite, vous devrez redémarrer mysqld.
Cela pourrait faire une énorme différence. Si vous êtes susceptible de subir une pression mémoire sur le système, vous souhaiterez probablement limiter la taille (généralement, les distributions Linux plafonnent tmpfs à 50% de la RAM totale par défaut) afin d'éviter d'échanger des segments de mémoire sur le disque, ou même pire une situation de MOO . Vous pouvez le faire en modifiant la ligne dans
/etc/fstab
:Vous pouvez également le redimensionner "en ligne". Par exemple:
Vous pouvez également passer à MySQL 5.6 - qui a des sous-requêtes performantes et des tables dérivées - et jouer un peu plus avec la requête. Je ne pense pas que nous verrons de grandes victoires dans cette voie, d'après ce que je vois.
Bonne chance!
la source