J'ai cette entrée dans le journal de requête lente:
# User@Host: user[Host] @ [ip]
# Thread_id: 1514428 Schema: db Last_errno: 0 Killed: 0
# Query_time: 2.795454 Lock_time: 0.000116 Rows_sent: 15 Rows_examined: 65207 Rows_affected: 0 Rows_read: 65207
# Bytsent: 26618
SET timestamp=1407511874;
select off.*,translated_title,translated_description
from ephpb2b_products off USE INDEX(id_viewed)
INNER JOIN ephpb2b_members mem
ON off.uid = mem.id
Left Join ephpb2b_product_language_new pol
ON off.id = pol.offer_id
and pol.language='en'
where off.approved=1
order by off.viewed
LIMIT 15;
Quand j'explique cette requête, c'est absolument bien.
mysql> explain select off.*,translated_title,translated_description from ephpb2b_products off USE INDEX(id_viewed) INNER JOIN ephpb2b_members mem ON off.uid = mem.id Left Join ephpb2b_product_language_new pol ON off.id = pol.offer_id and pol.language='en' where off.approved=1 order by off.viewed LIMIT 15;
+----+-------------+-------+--------+-------------------------+-------------+---------+---------------------------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+--------+-------------------------+-------------+---------+---------------------------+------+-------------+
| 1 | SIMPLE | off | index | NULL | id_viewed | 4 | NULL | 3 | Using where |
| 1 | SIMPLE | mem | eq_ref | PRIMARY | PRIMARY | 4 | db.off.uid | 1 | Using index |
| 1 | SIMPLE | pol | ref | offer_id,id_language | offer_id | 5 | db.off.id | 4 | |
+----+-------------+-------+--------+-------------------------+-------------+---------+---------------------------+------+-------------+
3 rows in set (0.17 sec)
Comment optimiser cette requête? Pourquoi expliquera-t-il 3 lignes et journal de requête lentement indique qu'il a examiné 65207 rangées.
Merci à tous pour vos idées. J'ai résolu cette question en divisant cette requête dans deux requêtes différentes. D'abord, j'ai demandé aux identifiants, puis passez ces identifiants à une autre table pour info.
select id from ephpb2b_products off INNER JOIN ephpb2b_members mem
ON off.uid = mem.id
where off.approved=1
order by off.viewed
LIMIT 15;
Puis:
select * from ephpb2b_product_language_new where offer_id IN ({ids from lasts query})
Cela fonctionne beaucoup mieux et n'agit pas étrange.
Afin de répondre à cette question, vous devez comprendre la colonne des lignes sur expliquer des moyens et la différence entre les calculs basés sur des statistiques et des statistiques post-exécution.
Lorsque vous exécutez Expliquez, la colonne des rangées vous indiquera, pour chaque accès de table, combien de lignes seront examinées en utilisant le filtre souhaité. Il existe deux façons de calculer que: soit une indice de plongée (qui devrait généralement vous donner des résultats exacts) ou en utilisant des statistiques approximatives que chaque moteur stocke indépendamment -UP à 5.6- pour chaque table. Bien que la première méthode est préférée lorsqu'elle peut être utilisée (filtres simples sur une colonne indexée unique), dans de nombreux cas, seule une approximation pourrait être utilisée dans l'ensemble, l'optimisateur de requête prendrait autant de temps que l'exécution de la requête elle-même.
Dans tous les cas, les lignes calculées sont les lignes calculées à lire (à ne pas être retournées) par accès à la table. Même s'il était exact (et plusieurs fois ont des différences avec plusieurs ordres de grossitude, mais il suffit toujours de l'optimiseur), il ne prévoit pas le nombre réel de lignes accessibles dans une jointure. Par exemple, si vous rejoignez la table A
(lecture exactement X
lignes) et tableau B
(lecture exactement Y
lignes), dans l'ordre A -> B
, le nombre réel de lignes LIRE sera: X + # of rows returned by A (<=X) multiplied by Y
, comme standard MySQL prend uniquement en charge les jointures de boucle imbriquées.
Le journal lent, comme les statistiques de gestionnaire, ou d'autres mécanismes de profilage vous indiquent que le nombre réel de lignes traitées et envoyées, car ces statistiques sont rassemblées après l'exécution, donc exacte.
En ce qui concerne votre cas particulier, EXPLAIN
est à blâmer car il montre que seules 3 lignes seraient numérisées pour le premier accès, lorsqu'il est en réalité, il peut effectuer une analyse complète de l'index (car elle utilise la clé uniquement pour le tri. ), qui est multiplié plus tard pour chaque join réalisée. Ne faites pas confiance à Expliquer. Vous pouvez utiliser:
FLUSH STATUS;
-- Execute your query here
SHOW STATUS like 'Hand%';
Pour vérifier le nombre réel et le type (accès PK, ref, balayage d'index, balayage de table) des opérations de ligne. Je l'utiliserais pour tester chaque accès de table individuellement.
Pour une aide plus spécifique, nous aurions besoin de la structure de la table de chaque table et de la sélectivité approximative de chaque condition de filtrage.
+1 à @junus pour les explications concernant EXPLAIN
, le journal des requêtes lentes et les lignes examinées.
Concernant -(( "Comment optimiser la requête" :=:
En supposant qu'il existe une relation de clé étrangère explicite entre le products
et la table members
, la jointure entre eux:
ephpb2b_products off
INNER JOIN ephpb2b_members mem
ON off.uid = mem.id
peut être converti en un LEFT JOIN
. Le FK assurera que les deux requêtes sont 100% équivalentes. Avoir cela à l'esprit, et que les clauses where
et order
:
WHERE off.approved=1
ORDER BY off.viewed
LIMIT 15
utilise uniquement des colonnes à partir de la table de base (products
), c'est-à-dire la table dans la partie "gauche" dans la clause FROM
, nous pouvons utiliser une sous-requête pour limiter d'abord les lignes et rejoindre l'autre deux tables, une technique que j'appelle
((( "Premier limit
, alors join
" :=:
SELECT
off.*,
pol.translated_title,
pol.translated_description
FROM
( SELECT p.* -- first limit
FROM ephpb2b_products AS p
WHERE p.approved=1
ORDER BY p.viewed
LIMIT 15
) AS off
LEFT JOIN -- then join
ephpb2b_members AS mem
ON off.uid = mem.id
LEFT JOIN
ephpb2b_product_language_new AS pol
ON pol.language = 'en'
AND pol.offer_id = off.id
ORDER BY
off.viewed ; -- no LIMIT required here
Avec un index sur ephpb2b_products (approved, viewed)
, la sous-requête sera assez efficace. Le reste du plan d'exécution n'importera pas que seules 15 lignes seront impliquées (et je suppose que vous avez des index dans les colonnes de jonction).
Un index supplémentaire sur phpb2b_product_language_new (language, offer_id)
peut également améliorer la poursuite de l'efficacité (mais non aveuglément, testez-le en premier. L'indice ci-dessus et la réécriture peuvent être suffisants pour améliorer la vitesse de manière drastique.)