Précédent   Forum des professionnels en informatique > Bases de données > MySQL > Requêtes
Requêtes Forum d'entraide sur les requêtes MySQL
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse Proposer ce sujet en actualité
 
Outils de la discussion
Publicité
'
Vieux 18/01/2012, 18h49   #1
Invité de passage
 
Homme
Inscription : juin 2011
Messages : 14
Détails du profil
Informations personnelles :
Sexe : Homme

Informations forums :
Inscription : juin 2011
Messages : 14
Points : 2
Points : 2
Par défaut optimisation de requête

Bonjour,

J'ai une requête qui me pose problème et que je n'arrive pas à optimiser.
La voici :

Code :
1
2
3
4
5
6
7
8
9
 
SELECT *
FROM  envois, task , messages, sites 
 
WHERE envois.idEnvoyeur = 1  AND envois.id = task.idEnvoi AND ( envois.STATUS = 'ENV' OR (envois.STATUS = 'PRG' AND envois.date_lance <= CURRENT_TIMESTAMP))
AND envois.idMessage = messages.id AND messages.idSite = sites.id 
 
ORDER BY envois.priority DESC, envois.id ASC, task.id ASC
LIMIT 0,150
voila ce que donne l'explain

Code :
1
2
3
4
5
6
7
8
 
id 	select_type 	TABLE 		type 	possible_keys 		KEY 		key_len 	ref 			rows 	Extra
 
1 	SIMPLE 		task 		ALL 	idEnvoi 		NULL 		NULL 		NULL 			754315 	USING TEMPORARY; USING filesort
 
1 	SIMPLE 		envois 		eq_ref 	PRIMARY,date_lance 	PRIMARY 	4 		task.idEnvoi 		1 	USING WHERE
1 	SIMPLE 		messages 	eq_ref 	PRIMARY 		PRIMARY 	4 		envois.idMessage 	1 	 
1 	SIMPLE 		sites 		eq_ref 	PRIMARY 		PRIMARY 	4 		messages.idSite 	1
INDEX sur la table task
Code :
1
2
3
4
 
Nom de l'index 	Type 		Cardinalité 		Champ
PRIMARY 		PRIMARY 	741565  	 	id
idEnvoi 		INDEX 	5  		 	idEnvoi
Je ne suis pas trés familier avec l'EXPLAIN mais,
ce que je ne comprend pas c'est que j'ai l'impression que la clé idEnvoi (index sur la table task) n'est pas utilisée, et que toute la table est balayée.

Est-ce que vous avez une idée ?
Merci pour votre aide.
zakaa est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 19/01/2012, 08h32   #2
Membre régulier
 
Homme Francis Lennert
Développeur informatique
Inscription : novembre 2011
Messages : 52
Détails du profil
Informations personnelles :
Nom : Homme Francis Lennert
Localisation : France, Paris (Île de France)

Informations professionnelles :
Activité : Développeur informatique

Informations forums :
Inscription : novembre 2011
Messages : 52
Points : 86
Points : 86
Bonjour,

Quels sont les types de données des différents IdEnvoi ??

As tu retiré les ORDER by pour faire explain sans eux ?
Grandal est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 19/01/2012, 09h26   #3
Invité de passage
 
Homme
Inscription : juin 2011
Messages : 14
Détails du profil
Informations personnelles :
Sexe : Homme

Informations forums :
Inscription : juin 2011
Messages : 14
Points : 2
Points : 2
Bonjour,

Les champs id sont en int(9).

J'ai déjà testé sans order by, le résultat est identique.

Sur la table task j'ai ajouté un index id,idEnvoi et idEnvoi,id.
Il me prend le idEnvoi,id en possible key mais ne l'utilise pas.

J'ai tenté le force index, pas de changement, il l'utilise pas.

Est-ce que vous pouvez me confirmer que je lis bien l'explain et qu'il balaye bien toute la table task, alors que l'utilisation d'un index devrait réduire cela ?
Au pire je m'attendais a ce qu'il lise 150.000 enregistrement (le nombre de task avec envois.id = task.idEnvoi ) et j'esperais que le limite reduirait encore cela ...

Comment est-ce que je peux m'assurer que les index sont bien construit ?
zakaa est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 19/01/2012, 11h53   #4
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 11 029
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
Localisation : France, Haute Garonne (Midi Pyrénées)

Informations professionnelles :
Activité : Ingénieur d'études en informatique
Secteur : Enseignement

Informations forums :
Inscription : août 2006
Messages : 11 029
Points : 18 331
Points : 18 331
Envoyer un message via MSN à CinePhil
Un peu de bonnes pratiques...

1) Évite la guerre des étoiles !

2) Les jointures s'écrivent depuis 1992 avec l'opérateur JOIN

3) Quand il y a plus d'une table dans une requête, celle-ci est plus facile à lire quand on utilise des alias.

4) Indenter correctement le code permet aussi de mieux comprendre la requête.

Voici votre requête récrite :
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT -- les colonnes nécessaires et pas étoile !
FROM envois e
INNER JOIN task t ON e.id = t.idEnvoi
INNER JOIN messages m ON e.idMessage = m.id
	INNER JOIN sites s ON m.idSite = s.id
WHERE e.idEnvoyeur = 1  
	AND 
	( 
		e.STATUS = 'ENV' 
		OR 
		(
			e.STATUS = 'PRG' 
			AND e.date_lance <= CURRENT_TIMESTAMP
		)
	)
ORDER BY envois.priority DESC, envois.id ASC, task.id ASC
LIMIT 0,150
Toutes les conditions de restriction (ce qu'il y a dans WHERE) portent sur la table "envois".
Y a t-il besoin dans le SELECT de colonnes provenant des autres tables ou certaines jointures sont-elles inutiles ?

Des index sur envois.idEnvoyeur, sur envois.STATUS et sur envois.date_lance seraient peut-être utiles pour ne pas balayer toute la table envois.

Il faut également que les colonnes messages.idSite, envois.idMessage et task.idEnvoi soient indexées puisqu'elles font fonction de clé étrangère, même si les tables sont peut-être en MyISAM et que les contraintes de clés étrangères ne sont pas utilisées.
__________________
Philippe Leménager. Ingénieur d'étude à l'École Nationale de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
À la maison comme au bureau, j'utilise Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 19/01/2012, 12h22   #5
Invité de passage
 
Homme
Inscription : juin 2011
Messages : 14
Détails du profil
Informations personnelles :
Sexe : Homme

Informations forums :
Inscription : juin 2011
Messages : 14
Points : 2
Points : 2
Merci pour ton aide CinePhil

1) J'adore la guerre des étoiles ... le film, mais dans ce cas précis je ne récupère que les champs dont j'ai besoin.J'ai mis * pour vous alléger la lecture de la requête.
J'ai fait plusieurs tests en modifiant les champs du select, au pire en ne gardant que le task.id, cela ne change rien.

2) J'ai toujours écrit mes requêtes avec "=".Surement une habitude de 'vieux' Coboliste, ou la quasi totalité des programmes sont bien plus vieux que 1992 ..
J'ai gardé cette sale habitude par la suite, mais promis je vais faire un effort.

3-4)Mea culpa, c'est codé 'proprement', j'ai un peu modifier ça pour le mettre sur le forum pensant que ce serait plus clair .. erreur d'appreciation.

Les tables envois, messages et sites contiennent un centaine de ligne au maximum chacune, l’accès ne pose pas de problème à priori.
Seule date_lance dans la table envois est indexé.

Les tables sont bien en MyISAM, sans contraintes de clé étrangère.

Je vais essayer ta requête et voir ce que ça donne.
Encore merci pour ton aide.
zakaa est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 19/01/2012, 12h46   #6
Invité de passage
 
Homme
Inscription : juin 2011
Messages : 14
Détails du profil
Informations personnelles :
Sexe : Homme

Informations forums :
Inscription : juin 2011
Messages : 14
Points : 2
Points : 2
Pas de modification sur l'explain avec la nouvelle requête.

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
SELECT
t.id AS idTask, t.uniqueId AS uniqueId, t.flag AS flag,
e.fichier AS fichier, e.id AS idEnvoi,  e.priority AS priority, e.STATUS AS STATUS, 
s.id AS idSite, s.txt AS txtSite,
m.id AS idMsg, m.txt AS txtMsg
 
FROM envois e
INNER JOIN task t ON e.id = t.idEnvoi
INNER JOIN messages m ON e.idMessage = m.id
	INNER JOIN sites s ON m.idSite = s.id
WHERE e.idEnvoyeur = 1  
	AND 
	( 
		e.STATUS = 'ENV' 
		OR 
		(
			e.STATUS = 'PRG' 
			AND e.date_lance <= CURRENT_TIMESTAMP
		)
	)
ORDER BY e.priority DESC, e.id ASC, t.id ASC
LIMIT 0,150
zakaa est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 19/01/2012, 14h17   #7
Membre régulier
 
Inscription : juillet 2003
Messages : 83
Détails du profil
Informations forums :
Inscription : juillet 2003
Messages : 83
Points : 90
Points : 90
Bonjour,

Peux tu faire un ANALYSE TABLE sur les tables en jeu et vérifier à nouveau si le plan d'exécution est plus favorable.

Combien d'enregistrements retourne cette requête sans le LIMIT ?

Cordialement

Selecta
selecta est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 19/01/2012, 14h19   #8
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 11 029
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
Localisation : France, Haute Garonne (Midi Pyrénées)

Informations professionnelles :
Activité : Ingénieur d'études en informatique
Secteur : Enseignement

Informations forums :
Inscription : août 2006
Messages : 11 029
Points : 18 331
Points : 18 331
Envoyer un message via MSN à CinePhil
Ma requête est identique à la tienne, seulement récrite différemment. Normal que tu ne constates pas de différence si tu n'as rien changé dans les index.

Au fait, elle s'exécute en combien de temps cette requête ?
__________________
Philippe Leménager. Ingénieur d'étude à l'École Nationale de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
À la maison comme au bureau, j'utilise Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 19/01/2012, 15h55   #9
Invité de passage
 
Homme
Inscription : juin 2011
Messages : 14
Détails du profil
Informations personnelles :
Sexe : Homme

Informations forums :
Inscription : juin 2011
Messages : 14
Points : 2
Points : 2
L'ANALYSE des tables a bien été fait, sans changement.
Je sais que seule la syntaxe diffère entre nos requêtes, mais j'ai déjà obtenu des gains significatifs dans d'autres cas juste en modifiant l'odre de la clause WHERE, donc je me suis dit pourquoi pas .

La table task contient entre 500.000 et 1.000.000 enregistrements.
La requête passe plusieurs fois dans un laps de temps donné, avec e.idEnvoyeur different.
Elle retourne entre 50.000 et 250.000 enregistrement sans le LIMITE.

Son temps d’exécution s’échelonne de 3 à 15s et dépend directement du nombre d'enregistrement qui serait retourné sans la clause LIMITE.

Autre chose que je trouve bizarre :
Le traitement lit 150 task, les traites, puis les DELETE une à une.
Le DELETE unitaire, via la clé primaire, sur la table task prend normalement 0.001s, mais certaine fois on a des pointes à 15s ! pour supprimer une ligne.Je ne sais pas si ça a un rapport avec le reste ...
QUICK et LOW_PRIORITY testé, sans changement.


Question subsidiaire :
Dans la pratique s.txt et m.txt sont identiques pour les 150 lignes retournées.
Il s'agit de VARCHAR(1500).
Ça demande du dev, mais est-ce que vous pensez que je gagnerais à retirer les tables messages et sites de cette requête, et d'en faire une autre juste pour récupérer ces champs ?
zakaa est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 19/01/2012, 16h09   #10
Modérateur
 
Avatar de CinePhil
 
Homme Philippe Leménager
Ingénieur d'études en informatique
Inscription : août 2006
Messages : 11 029
Détails du profil
Informations personnelles :
Nom : Homme Philippe Leménager
Âge : 48
Localisation : France, Haute Garonne (Midi Pyrénées)

Informations professionnelles :
Activité : Ingénieur d'études en informatique
Secteur : Enseignement

Informations forums :
Inscription : août 2006
Messages : 11 029
Points : 18 331
Points : 18 331
Envoyer un message via MSN à CinePhil
Tout ça ne me dit toujours pas si les tables sont correctement indexées !
Tu peux fournir la description complète des tables impliquées dans la requête (résultat e SHOW CREATE TABLE la_table) et l'explain complet de la requête ?

Citation:
Autre chose que je trouve bizarre :
Le traitement lit 150 task, les traites, puis les DELETE une à une.
Ce n'est pas une requête DELETE, tu dois donc parler d'autre chose. Quel traitement ?

En tout cas, si tu as une opération qui supprime des lignes sur la table task, ça bloque la table task. Les autres requêtes, dont ta requête SELECT ne peuvent pas s'exécuter tant que la table est bloquée.

Si tu expliques mieux ce que tu cherches à faire, on pourra t'aider plus efficacement. Lis la phrase en bleu de ma signature et applique son principe !
__________________
Philippe Leménager. Ingénieur d'étude à l'École Nationale de Formation Agronomique.
Mon blog sur la conception des BDD, le langage SQL, le PHP avec Zend Framework...
« Ce que l'on conçoit bien s'énonce clairement, et les mots pour le dire arrivent aisément ». (Nicolas Boileau)
À la maison comme au bureau, j'utilise Mandriva Linux ou Mageïa ! Soutenons l'industrie logicielle française !
Linuxiens, comptez-vous !
CinePhil est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 19/01/2012, 17h43   #11
Invité de passage
 
Homme
Inscription : juin 2011
Messages : 14
Détails du profil
Informations personnelles :
Sexe : Homme

Informations forums :
Inscription : juin 2011
Messages : 14
Points : 2
Points : 2
Pour les indexs toutes les tables ont PRIMARY KEY (`id`).

La table task à en plus KEY `idEnvoi` (`idEnvoi`)
Et j'ai ajouté un index sur id+idEnvoi et idEnvoi+id pour faire des tests.

Je n'ai plus accès aux tables jusqu’à demain, je vous fournirais le resultat du SHOW.

Pour l'explain complet de la requête il est sur le premier post.

Pour la description du traitement, je pensais être assez clair :
Citation:
Le traitement lit 150 task, les traites, puis les DELETE une à une.
Le DELETE unitaire, via la clé primaire, sur la table task prend normalement 0.001s, mais certaine fois on a des pointes à 15s ! pour supprimer une ligne.Je ne sais pas si ça a un rapport avec le reste ...
QUICK et LOW_PRIORITY testé, sans changement.
Donc la requete est lancée, les lignes récupérées sont lues une a une (fetch).
La task est traitée, puis supprimée unitairement, via la clé primaire :
DELETE FROM task WHERE id = $id
On lit ensuite la ligne suivante.

C'est le même traitement qui fait le select et le delete, séquentiellement.
Aucun autre traitement n’accède à cette table.
Je ne pense pas qu'il puisse y avoir de blocage dans ce cas ?

Le temps d’exécution de la requête est mesuré directement sur le traitement, en php.Le temps est pris juste avec la requête et juste après, pas d'autres traitement effectués.

EDIT:
Petite imprécision sur le delete, la mesure est prise avant et aprés l'appel à une fonction de ce type :

Code :
1
2
3
4
5
FUNCTION smart_mysql_query($sql){
	$res = mysql_query($sql) OR die(" TEXTE ");
	IF(!$res)	RETURN false;
	RETURN $res;
}
zakaa est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 26/01/2012, 09h46   #12
Invité de passage
 
Homme
Inscription : juin 2011
Messages : 14
Détails du profil
Informations personnelles :
Sexe : Homme

Informations forums :
Inscription : juin 2011
Messages : 14
Points : 2
Points : 2
J'ai résolu mon soucis en modifiant l'ORDER BY :

Code :
1
2
3
ORDER BY envois.priority DESC, envois.id ASC, task.id ASC
devient
ORDER BY task.idEnvoi ASC, task.id ASC
La requête utilise alors bien l'index idEnvoi + id de la table task, et s’exécute en moins d'1s.

Il n'y a plus de problèmes concernant le temps d’exécution des DELETE, ça par contre je ne me l'explique pas.
Je ferais des tests un peu plus tard pour comprendre (enfin, je le mets sur ma liste des tests à faire un peu plus tard ... ).

Merci pour le temps passé sur mon problème.
zakaa est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse Proposer ce sujet en actualité Cette discussion est résolue.
Outils de la discussion



Fuseau horaire GMT +2. Il est actuellement 06h51.


 
 
 
 
Partenaires

Hébergement Web