我有这些模特:
车
class Car < ActiveRecord::Base
has_many :car_services, dependent: :destroy
has_many :services, through: :car_services
end
car_service
class CarService < ActiveRecord::Base
belongs_to :car
belongs_to :service
validates_uniqueness_of :service_id, scope: :car_id
end
卡车
class Truck < ActiveRecord::Base
has_many :truck_services, dependent: :destroy
has_many :services, through: :truck_services
end
truck_service
class TruckService < ActiveRecord::Base
belongs_to :truck
belongs_to :service
end
服务
class Service < ActiveRecord::Base
has_many :car_services
has_many :cars, through: :car_services
has_many :truck_services
has_many :trucks, through: :truck_services
end
在控制器的动作中(汽车和卡车有不同的动作)我查询模型如下:
@cars = Car.find_by_sql("SELECT ...")
...
@trucks = Truck.find_by_sql("SELECT ...")
然后在各自的观点中:
<% @cars.each do |result| %>
<li data-services="<% result.services.each do |service| %><%= service.name %>,<% end %>">...</li>
...
<% end %>
此程序适用于cars
,速度非常快。
trucks
的不同操作,但程序相同:
<% @trucks.each do |result| %>
<li data-services="<% result.services.each do |service| %><%= service.name %>,<% end %>">...</li>
...
<% end %>
现在,这个程序非常慢。当我试图调试原因时(因为正如我所提到的,两个模型的程序相同,但对于汽车而言,即使在car
模型中的记录是600k记录和truck
中,它的工作也非常快)只有“300k”,我在控制台日志中发现以下信息:
...
Service Load (159.1ms) SELECT `services`.* FROM `services` INNER JOIN `truck_services` ON `services`.`id` = `truck_services`.`service_id` WHERE `truck_services`.`truck_id` = 35769
Service Load (166.8ms) SELECT `services`.* FROM `services` INNER JOIN `truck_services` ON `services`.`id` = `truck_services`.`service_id` WHERE `truck_services`.`truck_id` = 45681
Service Load (151.4ms) SELECT `services`.* FROM `services` INNER JOIN `truck_services` ON `services`.`id` = `truck_services`.`service_id` WHERE `truck_services`.`truck_id` = 50974
...
这怎么可能?为什么我还没有看到cars
的这些行,我看到truck
的这些行?我错过了什么吗?我在考虑MySQL表中缺少索引,但没有使用任何索引。
我整个星期六都在和这个问题作斗争,但我没有解决这个问题......
我非常感谢有关如何解决此问题的建议。
非常感谢你们。
答案 0 :(得分:1)
我将从建议开始:发布您的sql查询以便其他人了解您正在做什么并能够帮助您
要消除N + 1,您应该使用includes或preload
在您的控制器中执行以下操作:
@cars = Car.includes(:services).all
分别,
@trucks = Truck.includes(:services).all
你可以看到我更喜欢find_by_sql更清晰的语法。
当你跑步时:
@cars = Car.includes(:services).all
这将触发3个查询:
SELECT "cars".* FROM "cars";
SELECT "car_services".* FROM "car_services" WHERE "car_services".car_id IN (?, ?, ? ...);
SELECT "services".* FROM "services" WHERE "services".car_service_id IN (?, ?, ? ...);
如果您删除N + 1,您的页面加载速度会更快,但您应该检查所有密钥是否也已定义索引。
以上只是一个例子。您应该为我们发布完整的查询,以便准确了解您在做什么。而那些型号Car和Truck,它们看起来非常相似,也许它将是使用Single Table Inheritance的更好的解决方案。
你可以改写这个:
<% @trucks.each do |result| %>
<li data-services="<% result.services.each do |service| %><%= service.name %>,<% end %>">...</li>
<% end %>
这个
<% @trucks.each do |truck| %>
<%= content_tag :li, '...', data: {services: truck.services.map(&:name).join(', ')} %>
<% end %>
希望这会以某种方式帮助你。