Mysql
 sql >> Base de données >  >> RDS >> Mysql

La requête d'agrégation AVG() très simple sur le serveur MySQL prend un temps ridiculement long

Pour compter le nombre de lignes avec une date précise, MySQL doit localiser cette valeur dans l'index (ce qui est assez rapide, après tout c'est à cela que servent les index) puis lire les entrées suivantes de l'index jusqu'à ce qu'il trouve la prochaine date. Selon le type de données de esi , cela revient à lire quelques Mo de données pour compter vos 700 000 lignes. La lecture de certains Mo ne prend pas beaucoup de temps (et ces données peuvent même déjà être mises en cache dans le pool de mémoire tampon, selon la fréquence à laquelle vous utilisez l'index).

Pour calculer la moyenne d'une colonne qui n'est pas incluse dans l'index, MySQL utilisera à nouveau l'index pour trouver toutes les lignes pour cette date (comme avant). Mais en plus, pour chaque ligne qu'il trouve, il doit lire les données réelles de la table pour cette ligne, ce qui signifie utiliser la clé primaire pour localiser la ligne, lire quelques octets et répéter cela 700 000 fois. Ce "accès aléatoire" c'est beaucoup plus lente que la lecture séquentielle dans le premier cas. (Cela est aggravé par le problème que "certains octets" est le innodb_page_size (16 Ko par défaut), vous devrez donc peut-être lire jusqu'à 700 ko * 16 Ko =11 Go, par rapport à "quelques Mo" pour count(*); et selon la configuration de votre mémoire, certaines de ces données peuvent ne pas être mises en cache et doivent être lues à partir du disque.)

Une solution consiste à inclure toutes les colonnes utilisées dans l'index (un "index de couverture"), par ex. créer un index le date, 01 . Ensuite, MySQL n'a pas besoin d'accéder à la table elle-même et peut procéder, comme dans la première méthode, en lisant simplement l'index. La taille de l'index augmentera un peu, donc MySQL devra lire "un peu plus de Mo" (et effectuer le avg -opération), mais cela devrait encore être une question de secondes.

Dans les commentaires, vous avez mentionné que vous deviez calculer la moyenne sur 24 colonnes. Si vous souhaitez calculer la avg pour plusieurs colonnes en même temps, vous auriez besoin d'un index couvrant sur toutes, par ex. date, 01, 02, ..., 24 pour empêcher l'accès à la table. Sachez qu'un index qui contient toutes les colonnes nécessite autant d'espace de stockage que la table elle-même (et la création d'un tel index prendra beaucoup de temps), cela peut donc dépendre de l'importance de cette requête si elle vaut ces ressources.

Pour éviter la la limite MySQL de 16 colonnes par index , vous pouvez le diviser en deux index (et deux requêtes). Créer par ex. les index date, 01, .., 12 et date, 13, .., 24 , puis utilisez

select * from (select `date`, avg(`01`), ..., avg(`12`) 
               from mytable where `date` = ...) as part1
cross join    (select avg(`13`), ..., avg(`24`) 
               from mytable where `date` = ...) as part2;

Assurez-vous de bien documenter cela, car il n'y a aucune raison évidente d'écrire la requête de cette façon, mais cela pourrait en valoir la peine.

Si vous ne faites la moyenne que sur une seule colonne, vous pouvez ajouter 24 index séparés (le date, 01 , date, 02 , ...), bien qu'au total, ils nécessitent encore plus d'espace, mais pourraient être un peu plus rapides (car ils sont plus petits individuellement). Mais le pool de mémoire tampon peut toujours favoriser l'index complet, en fonction de facteurs tels que les modèles d'utilisation et la configuration de la mémoire, vous devrez donc peut-être le tester.

Depuis date fait partie de votre clé primaire, vous pouvez également envisager de remplacer la clé primaire par date, esi . Si vous trouvez les dates par la clé primaire, vous n'auriez pas besoin d'une étape supplémentaire pour accéder aux données de la table (puisque vous accédez déjà à la table), le comportement serait donc similaire à celui de l'index de couverture. Mais il s'agit d'un changement important pour votre table et peut affecter toutes les autres requêtes (qui utilisent par exemple esi pour localiser les lignes), il doit donc être considéré avec soin.

Comme vous l'avez mentionné, une autre option serait de créer un tableau récapitulatif dans lequel vous stockez des valeurs précalculées, en particulier si vous n'ajoutez ou ne modifiez pas de lignes pour des dates passées (ou si vous pouvez les maintenir à jour avec un déclencheur).