Подтвердить что ты не робот

Как я могу получить Rails для увеличения количества загрузок?

Это связано с вопросом год и изменение назад.

Я приведу пример вопроса, который должен работать из коробки, если у вас есть доступный sqlite3: https://github.com/cairo140/rails-eager-loading-counts-demo p >

Инструкции по установке (для основной ветки)

git clone git://github.com/cairo140/rails-eager-loading-counts-demo.git
cd rails-eager-loading-counts-demo
rails s

У меня есть полная запись в репозитории, но мой общий вопрос в этом.

Как я могу подсчитать количество загрузок Rails, чтобы свести к минимуму запросы db по всей доске?

Проблема n+1 возникает всякий раз, когда вы используете #count в ассоциации, несмотря на включение этой ассоциации через #includes(:associated) в ActiveRelation. Обходным путем является использование #length, но это хорошо работает только тогда, когда объект, на который он был вызван, уже загружен, не говоря уже о том, что я подозреваю, что он дублирует то, что уже сделали внутренности Rails. Кроме того, проблема с использованием #length заключается в том, что она приводит к неудачной перегрузке, когда ассоциация не была загружена для начала, и счет - это все, что вам нужно.

Из файла readme:

Мы можем уклониться от этой проблемы, запустив #length в массиве posts (см. приложение), который уже загружен, но было бы неплохо иметь счетчик также доступным. Это не только более последовательное; он обеспечивает путь доступа, который не обязательно требует загрузки сообщений. Например, если у вас есть частичное, которое отображает счетчик, независимо от того, что, но в половине случаев частичное вызывается с сообщениями, загруженными и половину времени без, вы столкнулись со следующим сценарием:

  • Использование #count
    • n COUNT запросы стиля, когда сообщения уже загружены
    • n COUNT запросы стиля, когда сообщения еще не загружены
  • Использование #length
    • Нулевые дополнительные запросы, когда сообщения уже загружены
    • n * запросы стиля, когда сообщения еще не загружены

Между этими двумя вариантами выбора нет доминирующего варианта. Но было бы неплохо пересмотреть #count, чтобы отложить до #length или получить доступ к длине, которая каким-то другим способом хранится за кулисами, чтобы мы могли иметь следующий сценарий:

  • Использование измененного #count
    • Нулевые дополнительные запросы, когда сообщения уже загружены
    • n COUNT запросы стиля, когда сообщения еще не загружены

Итак, какой правильный подход здесь? Есть ли что-то, что я забыл (очень, очень вероятно)?

4b9b3361

Ответ 1

Похоже, что наилучшим способом реализации такого средства может быть создание SQL-представлений (ref: здесь и здесь) для отдельных целевых объектов model-and-child-count; и связанные с ними модели ActiveRecord.

Возможно, вы сможете быть очень умными и использовать подклассы исходной модели в сочетании с set_table_name :sql_view_name, чтобы сохранить все исходные методы для объектов и, возможно, даже некоторые из их ассоциаций.

Например, скажем, мы добавили в ваш пример "Post.has_many: comments", как в ответе @Zubin выше; то можно было бы сделать:

   class CreatePostsWithCommentsCountsView < ActiveRecord::Migration
      def self.up
        #Create SQL View called posts_with_comments_counts which maps over 
        # select posts.*, count(comments.id) as comments_count from posts 
        #   left outer join comments on comments.post_id = posts.id 
        #   group by posts.id
        # (As zubin pointed out above.) 
        #*Except* this is in SQL so perhaps we'll be able to do further 
        # reducing queries against it *as though it were any other table.*
      end    
   end

   class PostWithCommentsCount < Post         #Here there be cleverness.
                                              #The class definition sets up PWCC 
                                              # with all the regular methods of 
                                              # Post (pointing to the posts table
                                              # due to Rails' STI facility.)

    set_table_name :posts_with_comment_counts #But then we point it to the 
                                              # SQL view instead.
                                              #If you don't really care about
                                              # the methods of Post being in PWCC
                                              # then you could just make it a 
                                              # normal subclass of AR::Base.
   end

   PostWithCommentsCount.all(:include => :user)  #Obviously, this sort of "upward
     # looking" include is best used in big lists like "latest posts" rather than
     # "These posts for this user." But hopefully it illustrates the improved 
     # activerecordiness of this style of solution.
   PostWithCommentsCount.all(:include => :comments) #And I'm pretty sure you 
     # should be able to do this without issue as well. And it _should_ only be 
     # the two queries.

Ответ 2

В качестве предлагаемого @apneadiving, counter_cache работает хорошо, потому что столбец счетчика автоматически обновляется при добавлении или удалении записей. Поэтому, когда вы загружаете родительский объект, счетчик включается в объект без необходимости доступа к другой таблице.

Однако, если по какой-либо причине вам не нравится этот подход, вы можете сделать это:

Post.find(:all,
          :select => "posts.*, count(comments.id) `comments_count`",
          :joins  => "left join comments on comments.post_id = posts.id")

Ответ 3

Я установил небольшой камень, который добавляет метод includes_count к ActiveRecord, который использует SELECT COUNT для извлечения количества записей в ассоциации, не прибегая к JOIN, который может быть дорогим (в зависимости от случая).

См. https://github.com/manastech/includes-count

Надеюсь, что это поможет!

Ответ 4

Альтернативный подход к одному из Zubin:

Post.select('posts.*, count(comments.id) `comments_count`').joins(:comments).group('posts.id')