web-dev-qa-db-fra.com

Pourquoi cette requête-cadre d'entité effectue-t-elle si mal dans MySQL?

Laissez-moi vous donner un peu de configuration. Nous avons une table InnoDB sur MySQL 5.1 avec près de 20 millions d'enregistrements, pas de clés étrangères et des index appropriés pour les requêtes que nous faisons. Nous utilisons la dernière version 6.3.5 MySQL pour le Cadre d'entité .NET. Normalement, nous sommes à utiliser pour traiter avec SQL Server et le cadre d'entité, mais sur ce projet, nous avons décidé de donner à MySQL un essai.

J'ai des théories sur la question mais laissez-moi d'abord faire la configuration du code

requête EF LINQ

var failsForAcct1001 = db.Failures.Where(x => x.AccountId == 1001);
/* farther down and later on in the code */
return failsForAcct1001.OrderBy(x => x.FailureId).Take(50);

code MySQL généré

Veuillez ignorer les noms de colonne qu'ils ne sont pas nessisaires pour comprendre le problème

select
    External1.FailureId,
    External1.col2,
    External1.col3,
from (select
          Inner1.FailureId,
          Inner1.col2,
          Inner1.col3,
      from Failures Inner1
      where External1.AccountId = 1001
    ) External1
order by External1.FailureId
limit 50

Ce SQL étant généré est très similaire à ce qui se passe dans SQL Server et SQL le gère avec un problème. Et ce n'est pas simplement une question de cadre d'entité, lorsque je prends cette requête et exécutez-la dans MySQL Workbench, cela est également expiré. Cependant, la requête suivante revient en moins d'une demi-seconde.

select
    External1.FailureId,
    External1.col2,
    External1.col3,
from Failures External1
where External1.AccountId = 1001
order by External1.FailureId
limit 50

Je suppose que ce problème de performance a à voir avec le fait que MySQL essaie d'exécuter la requête interne qui retire tous les enregistrements de la base de données, puis essaie de trier et de prendre 50, au lieu de le faire tout comme une seule opération comme la deuxième requête Est-ce que.

Je veux vraiment juste confirmer mes observations et demander s'il y a quelque chose que je peux faire pour obtenir la première instruction SQL à exécuter plus rapidement, sans modifier de quelque manière que ce soit.

6
Nick Berardi

Étape 1 - Obtenez un expliquer le plan , en particulier EXPLAIN EXTENDED vous montrera ce que SQL l'optimiseur de requête a réellement généré. peut-être Les index supplémentaires aideront - mais si vous "externalisez" votre génération SQL, vous êtes vraiment à la merci de tout ce qui était plus facile pour le développeur ORM, pas Quel était le mieux pour votre application. Une autre chose que vous pourriez essayer est en mémoire TEMP DB , car cela peut aider avec des sortes et des tables dérivées, que vous avez ...

5
Gaius

Mariahb 5.3 peut gérer de telles requêtes.

L'optimisation est couverte par le commutateur optimizer_switch='derived_merge=on' Dans la version 5.3.2 en cours, et cette optimisation deviendra défaut (aucun réglage nécessaire) dans la version imminente 5.3.3

Plus d'informations sur Optimizer en 5.

2
Vladislav Vaintroub

Je sais que les requêtes LINQ sont composables, mais avez-vous essayé de jouer avec l'ordre des opérations LINQ pour voir si cela pourrait affecter le générateur de requêtes? Cette question peut être mieux servie sur le débordement de la pile.

var failsForAcct1001 = db.Failures.Where(x => x.AccountId == 1001).OrderBy(x => x.FailureId).Take(50);

Votre problème est dû à la sous-requête.

Il y a deux problèmes avec ceci:

  • MySQL ne sait pas comment projeter les requêtes externes prédices, les limites, etc. à la requête interne, la requête interne finit donc étant complètement matérialisée (puis, en deuxième étape, filtrée sur la base des requêtes extérieures. ( http://dev.mysql.com/doc/refman/5.6/fr/subquerery-optimization.html )
  • La question est en outre aggravée par le fait que le connecteur MYSQL EF ne fait pas aussi bien qu'un travail d'optimisation/simplifiant la requête en tant que Microsoft. Le connecteur EF de Microsoft s'effondrera la requête interne et externe dans une requête (chaque fois que cela peut). MySQL ne fait pas cela avec la même précision que Microsoft.
1
Ryan Griffith