我最近加入了Ruby on Rails项目,其目标是改善一些运行时性能,因为有些请求需要花费太多秒(20+),我们希望减少它。对于长篇文章我很抱歉,但我正在寻求如何解决这么复杂问题的建议。
我首先要说的是,由于大多数这些请求后面都会有用户输入,因此将其移至后台任务将无助于问题的本质,因为用户仍需等待响应。另外我要补充一点,在这个时候我不能广泛地改变模型架构,只是可以增强它。
说,快速概述相关模型:
例如:
class A < ActiveRecord::Base
has_many :bs, :inverse_of => a
has_many :cs, :inverse_of => a
end
class B < ActiveRecord::Base
belongs_to :a, :inverse_of => bs
has_many :ds, :inverse_of => b
scope :by_active_status, lambda { where(:status => ‘active’) }
end
class C < ActiveRecord::Base
belongs_to :a, :inverse_of => cs
has_many :es, :inverse_of => c
end
class D < ActiveRecord::Base
belongs_to :b, :inverse_of => ds
...and so on
end
class E < ActiveRecord::Base
belongs_to :c, :inverse_of => es
...and so on
end
作为一个例子,我们有一些比较实用程序,它比较了所有级别的2个根对象。这不是浅层比较,有些领域具有比较意义,有些领域没有,例如,如果一个对象有一个名称,描述&amp;价格与价格无关,因此,即使只有名称和价格,也认为2个对象是相等的。描述匹配。请注意,比较方法在内部使用上述范围。
现在性能不好主要是因为每个对象都是由DB(MySql)自己加载的,这意味着有很多数据库查询。我的第一件事是对整个层次结构使用eager-load('includes'),但是由于使用这些范围它没有帮助。
例如:A.where(:id => some_id).includes([{:b => d}, {:c => e}])
但是在比较A
的方法中,它只需要比较活动的子B
,因此它使用范围'by_active_status',从而执行另一个数据库查询(在哪里)另外,Ds及其关联不加载等。)。
我找到了类似这些示例的代码:a.bs.by_active_status
或c.bs.by_active_status
我发现自己更改了使用这些范围的地方,在已经加载关联的情况下,它将从已加载对象的数组中选择相关对象,以及在何处应该使用关联(其他代码流到那里)。它有助于短期,但这看起来非常难看,而不是红宝石式。
我必须承认,最初让我感到惊讶的是,这些“无辜”的范围隐藏着这样的性能问题。 那就是目前的情况。
有关此问题的方法的任何建议吗?
=== EDITED ===
根据要求,这是一个等效的示例schema.rb:
create_table "as", :force => true do |t|
t.string "some_string"
t.integer "some_int"
end
create_table "bs", :force => true do |t|
t.integer "a_id", :null => false
t.string "some_string"
t.integer "some_int"
end
add_index "bs", ["a_id"], :name => "index_bs_on_a_id"
create_table "cs", :force => true do |t|
t.integer "a_id", :null => false
t.string "some_string"
t.integer "some_int"
end
add_index "cs", ["a_id"], :name => "index_cs_on_a_id"
create_table "ds", :force => true do |t|
t.integer "b_id", :null => false
t.string "some_string"
t.integer "some_int"
end
add_index “ds", [“b_id"], :name => "index_ds_on_b_id"
create_table “es", :force => true do |t|
t.integer “c_id", :null => false
t.string “some_string"
t.integer “some”_int
end
add_index “es", [“c_id"], :name => "index_es_on_c_id"