改进查询/查看加载时间[Ruby on Rails]

时间:2011-12-27 17:18:06

标签: ruby-on-rails performance optimization view

我正在使用我的Rails应用程序的一个页面遇到一些非常慢的加载时间。我在提高性能方面取得了一些进展,但没有我希望的那么多。我想知道我是否正在做一些愚蠢的事情让自己感到悲伤,或者是否有人可以提供建议以更好地优化我的模型/迁移/查询/视图。我正在运行Rails 3.1并使用PostgreSQL。这是我到目前为止所尝试的:

  • 添加了包含(:items =>:category)到控制器的查询,作为尝试提前获取所有ItemCategories,而不是在outfits_helper需要时运行其他查询。
  • 将partials渲染为集合,而不是手动迭代数组/关系
  • 向有问题的控制器操作/视图访问的表添加索引
    • outfits table:user_id和category_id的索引
    • 项目表:user_id和category_id的索引
    • outfit_items表:item_id,outfit_id和[outfit_id,item_id]的索引

以下是我的代码的相关部分:

控制器

# outfits_controller.rb

NUM_OUTFITS_PER_PAGE = 8

def other_users_outfits
  # using Kaminari for pagination
  @outfits = Outfit.does_not_belong_to_user(current_user).in_category_with_id(category_id).includes(:items => :category).page(params[:page]).per(NUM_OUTFITS_PER_PAGE)
end

模型

# outfit.rb

has_many :items, :through => :outfit_items
belongs_to :category, :class_name => 'OutfitCategory'

scope :does_not_belong_to_user, proc {|user| where('user_id != ?', user.id) }

scope :in_category_with_id, proc {|cat_id|
  if cat_id.blank?
    scoped
  else
    where(:category_id => cat_id)
  end
}

# outfit_item.rb

belongs_to :item
belongs_to :outfit

# item.rb

has_many :outfit_items
has_many :outfits, :through => :outfit_items
belongs_to :category, :class_name => 'ItemCategory'

# item_category.rb

has_many :items, :foreign_key => 'category_id'

outfit_category.rb

has_many :outfits, :foreign_key => 'outfit_id'

迁移

# create_outfits.rb

def self.up
  create_table :outfits do |t|
    t.column :category_id, :integer
    t.column :user_id, :integer
    t.timestamps
  end

  add_index :outfits, :category_id
  add_index :outfits, :user_id
end

# create_outfit_items.rb

def self.up
  create_table :outfit_items, :id => false do |t|
    t.column :item_id, :integer
    t.column :outfit_id, :integer
    t.timestamps
  end

  add_index :outfit_items, :item_id
  add_index :outfit_items, :outfit_id
  add_index :outfit_items, [:outfit_id, :item_id]   
end

# create_items.rb

def self.up
  create_table :items do |t|
    t.column :category_id, :integer
    t.column :user_id, :integer
    t.timestamps
  end

  add_index :items, :category_id
  add_index :items, :user_id
end

# create_item_categories.rb
def self.up
  create_table :item_categories do |t|
    t.column :name, :string
    t.column :category_type, :string
    t.timestamps
  end
end

# create_outfit_categories.rb

def self.up
  create_table :outfit_categories do |t|
    t.column :name, :string, :limit => 100, :null => false
    t.timestamps
  end
end

浏览

# other_users_outfits.html.haml

= render :partial => 'other_users_outfit', :collection => outfits, :as => :outfit

# _other_users_outfit.html.haml

.outfit
  .primary-items
    = render :partial => 'other_users_outfit_primary_item', :collection => primary_outfit_items_top_to_bottom(outfit), :as => :item
  .secondary-items
    = render :partial => 'other_users_outfit_secondary_item', :collection => secondary_outfit_items(outfit), :as => :item

# _other_users_outfit_primary_item.html.haml

= image_tag item.image_url(:outfit_item), :class => 'primary-outfit-item'

# _other_users_outfit_secondary_item.html.haml

= image_tag item.image_url(:baby_thumb), :class => 'secondary-outfit-item'

辅助

# outfits_helper.rb

def primary_outfit_items_top_to_bottom(outfit)
  primary_items = []
  primary_items.push(outfit_item_in_category(outfit, 'Tops'))
  primary_items.push(outfit_item_in_category(outfit, 'Full-lengths'))
  primary_items.push(outfit_item_in_category(outfit, 'Bottoms'))
  primary_items.push(outfit_item_in_category(outfit, 'Footwear'))
  primary_items.compact
end

def secondary_outfit_items(outfit)
  outfit.items.select{|item| item.category.category_type == 'secondary' }
end

def outfit_item_in_category(outfit, cat_name)
  outfit.items.select{|item| item.category.name == cat_name }.first
end

控制台输出

User Load (1.0ms)  SELECT DISTINCT users.id, users.* FROM "users" WHERE "users"."id" = 3 LIMIT 1
(0.5ms)  SELECT COUNT(count_column) FROM (SELECT 1 AS count_column FROM "outfits" WHERE (user_id != 3) LIMIT 8 OFFSET 0) subquery_for_count 

Outfit Load (0.7ms)  SELECT DISTINCT outfits.id, outfits.* FROM "outfits" WHERE (user_id != 3) ORDER BY outfits.created_at DESC LIMIT 8 OFFSET 0

OutfitItem Load (0.6ms)  SELECT "outfit_items".* FROM "outfit_items" WHERE "outfit_items"."outfit_id" IN (28, 27, 26, 25, 24, 23, 22, 21)

Item Load (2.2ms)  SELECT DISTINCT items.id, items.*, "items".* FROM "items" WHERE "items"."id" IN (18, 20, 23, 7, 6, 30, 4, 1, 17, 5, 15, 12, 9, 29, 10, 19, 3, 8, 13) ORDER BY items.created_at DESC

ItemCategory Load (0.5ms)  SELECT "item_categories".* FROM "item_categories" WHERE "item_categories"."id" IN (4, 6, 2, 1, 3, 7)

Rendered outfits/_other_users_outfit_secondary_item.html.haml (1.2ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.1ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (2.8ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (1.4ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.9ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (1.2ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.3ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.9ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (2.7ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.8ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.7ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (1.2ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (1.1ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.3ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (2.2ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.8ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.3ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (3.4ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.3ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.0ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (2.6ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.3ms)
Rendered outfits/_other_users_outfit_secondary_item.html.haml (0.3ms)
Rendered outfits/_other_users_outfit_primary_item.html.haml (2.1ms)
Rendered outfits/_other_users_outfit.html.haml (56.3ms)
   (0.5ms)  SELECT COUNT(*) FROM "outfits" WHERE (user_id != 3)
Rendered outfits/other_users.html.haml within layouts/application (2073.5ms)

Role Load (0.4ms)  SELECT "roles".* FROM "roles" WHERE "roles"."name" = 'admin' LIMIT 1
   (0.4ms)  SELECT 1 FROM "roles" INNER JOIN "roles_users" ON "roles"."id" = "roles_users"."role_id" WHERE "roles_users"."user_id" = 3 AND "roles"."id" = 1 LIMIT 1

Rendered layouts/_top_nav.html.haml (1.0ms)
   (0.6ms)  SELECT COUNT(*) FROM "items" WHERE "items"."user_id" = 3

Rendered layouts/_points_display.html.haml (5.7ms)

Outfit Load (0.6ms)  SELECT DISTINCT outfits.id, outfits.* FROM "outfits" WHERE "outfits"."user_id" = 3 ORDER BY outfits.created_at DESC

OutfitCategory Load (0.3ms)  SELECT "outfit_categories".* FROM "outfit_categories"

非常感谢任何帮助!

1 个答案:

答案 0 :(得分:0)

我唯一看到可以改进的是助手中的代码。你有没有理由这样做:

def outfit_item_in_category(outfit, cat_name)
  outfit.items.select{|item| item.category.name == cat_name }.first
end

而不是将此代码推送到Item模型中的范围?如果你有很多项目,那么你基本上是对它们进行SELECT *然后在Ruby中过滤。