2012-06-27 5 views
0

У меня есть следующий запрос ActiveRecord. Он работал нормально, когда количество сообщений было 50 в моей тестовой среде, но когда мы ввели его в производство, а количество сообщений выросло до 5000, время ответа приблизилось к 30 секундам. Нехорошо.Плохо масштабируемые рельсы Запрос ActiveRecord

Как я могу сделать этот запрос более эффективно, так что держите его быстрым, так как количество сообщений растет. Запрос находит все предупреждения из всех сообщений, если они еще не завершены.

class AlertsController < ApplicationController 
    before_filter :get_user 
    respond_to :json, :html 

def index 
    @messages = current_user.messages.where(:active => true).order("created_at ASC") 

    @alerts = Array.new 
    @messages.each do |message| 
     if (message.alerts.count > 0) 
      @alerts = @alerts + message.alerts.where(:completed => false) 
     end 
    end 

    respond_to do |format| 
     format.html 
     format.json 
    end 
    end 

end 


class Alert < ActiveRecord::Base 
    belongs_to :message 

class Message < ActiveRecord::Base 
    has_many :alerts, dependent: :destroy 
+0

Какие индексации вы используете на ваших столах? – robbrit

+0

good catch - в таблице предупреждений нет ссылки на сообщение. добавит его и обновит вашу производительность. – bluedevil2k

+0

В качестве примечания использование '[]' для создания пустого массива более традиционно, чем использование 'Array.new'. Также вы можете использовать '+ =' для добавления вещей в массив вместо '@array = @array + ...'. – tadman

ответ

3

здание от ответа Джесси, немного лучше было бы создать область только выбрать pending_alerts , таким образом вы пропускаете меньше данных и позволяете базе данных выполнять большую часть работы.

class Message < ActiveRecord::Base 
    scope :pending_alerts, lambda { 
    alerts.where(:completed => false) 
    } 
} 

Тогда в контроллере:

@messages = current_user.messages.includes(:pending_alerts).where(:active => true).order("created_at ASC") 
@alerts = @messages.map { |m| m.alerts } 
+0

ohhhh приятно. Мне нравится идея включения области; никогда не было этого! –

+0

Если вы используете определенный набор условий более одного раза, почти всегда полезно определить для них 'scope'. – tadman

+0

Вам действительно не нужна лямбда в этом конкретном случае. Вы можете просто сказать: scope: pending_alerts, join (: warn) .where ("warnandspleted = false") – Salil

1

Это проблема с n + 1. Документация здесь (поиск Solution to N + 1 queries problem)

@messages = current_user.messages.includes(:alerts).where(:active => true).order("created_at ASC") 

Затем, чтобы сделать @alerts проще:

@alerts = @messages.map do |message| 
    message.alerts.select {|alert| !alert.completed} 
end 
+1

Вместо того, чтобы делать 'select' на выбранных оповещениях, почему бы не пройти условие, которое исключает их в первую очередь? – tadman

+0

Стараясь свой второй блок, я получаю эту ошибку - Отображение приложение/просмотров/предупреждения/index.json.rabl где линия # 1 поднял:

can't convert Array into Hash
bluedevil2k

+0

@tadman Мне нравится объем pending_alerts от chrismohr для вашего предложения –

Смежные вопросы