7
votes

Demandes supplémentaires énumérées par miniprofiler

dans mon action de contrôleur, i inclus code> Toutes les associations nécessaires à la vue, pour éviter plusieurs appels à la base de données. (J'essaie d'isoler la couche de vue pour rendre les données collectées par le contrôleur).


i'v a découvert que la vue communique toujours avec la base de données (17 requêtes): miniprofiler p> blockQuote>

ces 17 requêtes supplémentaires code> strong> ne sont pas nécessaires. Étant donné que j'ai testé les requêtes du contrôleur de la console et collectionne avec succès toutes les données nécessaires à la partielle _dropdown code> (dans 5 requêtes forte>) sans aucune nouvelle communication de base de données. p>

voici le Query dans mon contrôleur, cela empêche d'éviter le N + 1 code> problème. (Y compris toutes les variables appelées dans la vue) p>


voici le code déroulant: h3> xxx pré>

sortie de la console: H3>
2.0.0-p353 :006 > ms = Message.dropdown_for(3).all
  Message Load (1.2ms)  SELECT "messages".* FROM "messages" LEFT JOIN messages AS m ON messages.id != m.id 
 AND m.conversation_id = messages.conversation_id 
 AND messages.created_at < m.created_at INNER JOIN conversation_participants AS cp ON cp.conversation_id = messages.conversation_id AND cp.user_id = 3 WHERE (m.id IS NULL) ORDER BY cp.seen , cp.updated_at DESC LIMIT 5
  User Load (0.7ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (6, 4, 5)
  Conversation Load (0.4ms)  SELECT "conversations".* FROM "conversations" WHERE "conversations"."id" IN (4, 2, 3)
  ConversationParticipant Load (0.2ms)  SELECT "conversation_participants".* FROM "conversation_participants" WHERE "conversation_participants"."conversation_id" IN (4, 2, 3)
  User Load (0.6ms)  SELECT "users".* FROM "users" WHERE "users"."id" IN (6, 3, 4, 5)
 => [#<Message id: 8, body: "saSasa", sender_id: 6, conversation_id: 4, sent: true, attachment_id: nil, attachment_type: nil, created_at: "2014-11-17 16:05:40", updated_at: "2014-11-17 16:05:40">, #<Message id: 2, body: "asdnas dagsdashjdg jahs d", sender_id: 4, conversation_id: 2, sent: true, attachment_id: nil, attachment_type: nil, created_at: "2014-11-17 11:32:36", updated_at: "2014-11-17 11:32:36">, #<Message id: 6, body: "SADASD A DSA ", sender_id: 5, conversation_id: 3, sent: true, attachment_id: nil, attachment_type: nil, created_at: "2014-11-17 13:43:34", updated_at: "2014-11-17 13:43:34">] 

2.0.0-p353 :007 > ms.first.conversation.conversation_participants.select{|cp| cp.user_id != 3}.first.user
 => #<User id: 6, first_name: "Ddsfsd", middle_name: nil, last_name: "Fsdfsd", photo: nil, email: "1@k.com", encrypted_password: "$2a$10$5sGIb2DbQ1ctMrTzD3AJ0uV18hhiC5Ei1wcfE7MSAvRU...", reset_password_token: nil, reset_password_sent_at: nil, remember_created_at: nil, sign_in_count: 1, current_sign_in_at: "2014-11-17 15:27:06", last_sign_in_at: "2014-11-17 15:27:06", current_sign_in_ip: "127.0.0.1", last_sign_in_ip: "127.0.0.1", confirmation_token: nil, confirmed_at: "2014-11-17 15:27:48", confirmation_sent_at: "2014-11-17 15:27:05", unconfirmed_email: nil, failed_attempts: 0, unlock_token: nil, locked_at: nil, authentication_token: nil, created_at: "2014-11-17 15:27:05", updated_at: "2014-11-17 15:27:48", slug: "ddsfsd_fsdfsd"> 

2.0.0-p353 :008 > ms.count
 => 3 


11 commentaires

En fait, j'ai essayé de déconnecter de la console, tout fonctionne comme prévu, mais je reçois une erreur lorsque je l'ajoute dans mon contrôleur (accéder aux mêmes attributs) du serveur.


Pourriez-vous s'il vous plaît poster le code pour vos fichiers d'index et d'affichage déroulant et l'action de conversation du contrôleur?


Les Slopes Rails vous permettent de créer des requêtes et d'exécuter lorsque vous les utilisez. Pour exécuter, vous pouvez appeler .Toutez. Avez-vous essayé d'ajouter .all à la fin de votre requête dans l'action du contrôleur?


Oui, je l'ai fait, aussi to_a . La chose est la précharge semble prendre plus de questions que cela devrait.


Depuis le journal, pourriez-vous coller les 17 requêtes que vous souhaitez éliminer / ne pensez-vous pas qu'ils sont nécessaires? Vous pouvez simplement coller une des questions s'ils sont tous à peu près les mêmes.


J'ai posté la même requête de la console, indiquant aucune communication de base de données supplémentaire lors de l'appel des associations.


Je m'attendais à ces 5 déclarations sélectionnées, mais ne voyez pas les 17 requêtes supplémentaires. Avez-vous des filtres (avant / après) qui relient-vous à votre action de contrôleur ou à tout autre code dans votre action de contrôleur?


Ces requêtes générées disparaissent lorsque je commente la ligne @Messages . Je commence à penser que c'est un problème avec le profileur ou la version de mes rails.


Au lieu de comparer message.sender à actuel_user , comparer message.sender_id == actuel_user.id : une requête moins par message? :) Aussi: stockez le résultat dans une variable et effectuez le test qu'une seule fois.


L'expéditeur est déjà préchargé, car j'ai besoin de l'expéditeur


Merci à tout le monde, j'ai enfin trouvé la solution à mon problème. J'apprécie vraiment votre effort.


6 Réponses :


1
votes

Cela peut être appelé "N + 1", cela se produit en raison de la chargement paresseux. Je ne peux pas dire avec certitude sans journal d'application. Vous pouvez utiliser le chargement impatient comme décrit ici .


1 commentaires

J'utilise déjà précharge et eager_load pour collecter mes données dans le contrôleur.



3
votes

Vous pouvez essayer BUNLET GEM qui vous dira qu'il y a une prolème N + 1 dans la requête. S'il n'y a pas de problème de problème N + 1, vous devriez essayer d'implémenter la mise en cache de fragments.


2 commentaires

Comment cela pourrait-il se produire que du serveur (dans la console tout va bien)?


Essayez les rails-footnotes GEM qui afficheront toutes les requêtes de vues comme contrôleur de la ligne avec des numéros de ligne. Github.com/josevalim/rails-footNotes



2
votes

Question simple. Avez-vous essayé de mettre un. to_a à la fin de votre méthode appelez? Comme @ messages.a_a ?


2 commentaires

Ceci est vraiment embrassant que je n'ai pas essayé to_a . Maintenant, le 17 déplacé sur le contrôleur et le 5 a disparu, si je pensais à éliminer les inutiles, je vais vous donner la prime. Merci pour l'indice.


Heureux de pouvoir vous aider, à la fin! Faites le nous savoir!



2
votes

Je vérifierais le journal pour voir ce que ces 17 questions sont, ou peut-être en cliquant sur le lien 17 SQL affichera ces questions. À partir de là, vous pourrez peut-être voir que vous avez oublié de inclut une table qui provoque le problème N + 1.

EDIT:

Comme indiqué dans la section "Chargement paresseux" de Ce site , vous Peut ajouter .all à la fin de votre requête dans votre action de contrôleur pour déclencher son exécution et empêcher la requête d'exécuter de manière paresseuse à votre vue. Comme mentionné dans mon commentaire, Rails Slopes vous permet de construire des requêtes et d'exécuter lorsque vous les utilisez. Pour exécuter, vous pouvez appeler .Toutez. Dans les rails 4, vous pouvez utiliser .charger pour exécuter la requête dans le contrôleur.


3 commentaires

Dans Rails 4+, .Alle construira un objet objet et ne déclenche pas la requête instantanément. Au lieu de cela, vous devrez appeler to_a sur votre requête.


TRUE, Toutefois, pour les rails 4, j'utiliserais all.load pour renvoyer une action activeecordrelation sur to_a .


Génial, ne le savait pas. Merci!



1
votes

Votre requête n'est pas bien formulée. Vous devez soit utiliser Inclus ou des jointures.

Casser votre requête en deux comme suit: P>

message_ids = Message.joins("LEFT JOIN messages AS m ON messages.id != m.id 
          AND m.conversation_id = messages.conversation_id 
          AND messages.created_at < m.created_at")
   .where('m.id IS NULL')
   .joins("INNER JOIN conversation_participants AS cp 
          ON cp.conversation_id = messages.conversation_id 
          AND cp.user_id = #{user_id}")
   .order("cp.seen, cp.updated_at DESC")
   .limit(5).map(&:id)

messages = Message.includes(:sender).
    includes(conversation: [{conversation_participants: :user}]).
    where(id: message_ids)


3 commentaires

Eh bien, je respecte votre approche, mais cela ajoute une autre requête. Je ne peux pas comprendre exactement quelle est la différence entre la console et le serveur.


Je n'ai rien dit sur la console et le serveur. Fondamentalement, il n'est pas possible d'utiliser des jointures avec une limite d'Activerecords. Ce que je vous ai fourni est la solution la meilleure et la plus correcte. Vous avez juste une requête supplémentaire au lieu des 17 que vous avez à l'origine et c'est ainsi que vous développez ces solutions.


Eh bien, votre approche les a faite 18. Et ce n'est pas une limitation si vous regardez la sortie de la console.



6
votes

* Débogage

Bien après avoir débogué tous les facteurs possibles pouvant causer ce problème. J'ai essayé de définir config.cache_classes sur true, dans mon développement.rb . Cela a supprimé avec succès toutes les requêtes supplémentaires.

J'ai conclu que (par défaut), le schéma n'est pas chargé pour aucun modèle lorsque les classes ne sont pas cachées. En d'autres termes, lorsque config.cache_classes est défini sur false , le schéma de chaque modèle est chargé pour chaque demande de requête distincte.

Voici un problème similaire Méthode de colonne_DeFinitions appelée avant et après chaque instruction SQL sur PostgreSQL .

* Conclusion

cache_classes doit être défini sur false dans développement environnement. Ignorer les requêtes internes supplémentaires de l'adaptateur de connexion PostgreSQL chargement du schéma pour chaque modèle car il ne va pas affecter votre Environnement de production (production a config.cache_classes défini sur vrai ).


5 commentaires

Config.Cache_clases n'a rien à voir avec les requêtes. Ce que j'ai fourni est la solution correcte et ne doit pas causer de 18 ans.


@Arkhitech Votre solution n'est rien d'autre qu'une requête supplémentaire. Je l'ai testé bien que je sache sa mauvaise solution. J'utilise PostgreSQL, à la caisse du problème similaire.


hmmm ... Je n'ai pas la configuration de l'environnement pour tester manuellement cela, mais config.cache_classes ne peut aucun effet sur les requêtes.


Chargement du schéma de classe (modèle) apparaît comme une requête en mini-profileur


Le problème a été signalé au mini-profileur ici: link1 et link2