Rails执行太多的SQL
我已经开始注意到,我的Rails应用程序生成太多的SQL语句,其中很多都不是必需的。我记得在某个地方读这可能是一个问题,而且现在随着我的数据增长,它明显地减缓了应用程序的速度。Rails执行太多的SQL
例如,我有发布有轨道。艺术家可以分配到曲目和发行。当我加载一个只有8首曲目的发行版时,它似乎正在贯穿数据库中的每一首曲目,以找到这些关系!?!
例如,请参见下文,这是一个非常小的示例,但这些轨道都没有与发布实际关联。它正在通过DB中的每个轨道!
任何常规指针?
Artist Load (0.6ms) SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 12
Artist Load (0.6ms) SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 19
Artist Load (0.6ms) SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 21
Artist Load (0.9ms) SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 22
Artist Load (0.7ms) SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 23
Artist Load (0.6ms) SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 24
Artist Load (0.9ms) SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 25
Artist Load (1.0ms) SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 26
Artist Load (0.6ms) SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 27
Artist Load (0.9ms) SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 28
Artist Load (0.6ms) SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 29
Artist Load (0.6ms) SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 30
Artist Load (0.6ms) SELECT `artists`.* FROM `artists` INNER JOIN `artists_tracks` ON `artists`.`id` = `artists_tracks`.`artist_id` WHERE `artists_tracks`.`track_id` = 31
这里涉及到的车型:
class Artist < ActiveRecord::Base
has_many :artist_releases
has_many :releases, :through => :artist_releases
has_many :artists_tracks
has_many :tracks, :through => :artists_tracks
end
class ArtistRelease < ActiveRecord::Base
belongs_to :artist
belongs_to :release
end
class ArtistsTrack < ActiveRecord::Base
belongs_to :artist
belongs_to :release
belongs_to :track
end
class Release < ActiveRecord::Base
has_many :artist_releases
has_many :artists, :through => :artist_releases
accepts_nested_attributes_for :artists, :reject_if => lambda { |a| a[:name].blank? }
accepts_nested_attributes_for :artist_releases
has_many :releases_tracks, :dependent => :destroy
has_many :tracks, :through => :releases_tracks, :order => "releases_tracks.position"
accepts_nested_attributes_for :tracks, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => :true
accepts_nested_attributes_for :releases_tracks
end
class ReleasesTrack < ActiveRecord::Base
default_scope :order => 'releases_tracks.position ASC'
acts_as_list :scope => :release_id
belongs_to :release
belongs_to :track
end
class Track < ActiveRecord::Base
has_many :releases_tracks, :dependent => :destroy
has_many :releases, :through => :releases_tracks
has_many :artists_tracks
has_many :artists, :through => :artists_tracks
accepts_nested_attributes_for :artists, :reject_if => lambda { |a| a[:name].blank? }
accepts_nested_attributes_for :artists_tracks
end
从Rails正在生成的查询,它看起来像你正在做这样的事情,它单独加载每个艺术家,当你提到它:
release.tracks.each{ |t| t.artist }
你应该急切地加载的艺术家,这样在单个查询它们预装了:
release.tracks.includes(:artist).each{ |t| t.artist }
的includes
方法是非常灵活的,并允许您急切地加载多个协会,甚至嵌套协会:
Release.first.includes(:releases_tracks => {:track => :artist})
这将加载第一个版本,那么其所有ReleaseTracks,然后他们所有的曲目,以及所有曲目的艺术家只需4次查询(每桌1张)。这比单独加载每条记录效率更高。
有关Eager Loading Associations in the Active Record Query interface guide部分的详细信息。
啊! 'release.tracks.each {| t | t.artist}'看起来很熟悉,所以我会看看我是否可以按照你的建议重构。谢谢! – Raoot
实际上并不完全一样,在我看来,tracklisting是通过'',然后艺术家用'。我应该在其他地方做点什么吗? – Raoot
我怀疑当你最初加载'@ release'时想使用'includes',就像:'@release = Release.foo.includes(:releases_tracks => {:track =>:artists})'但是如果你更新您的问题与更多关于您的模型的信息(以及您在评论中发布的代码)我或其他人将能够提供更好的答案。 – georgebrock
可能希望包含关系的基础知识(模型定义)。 –
确保您不会循环查询。所有这些查询都应该被转换成一个IN语句 – Limey
@DaveNewton - 我会尽量在一秒内添加,所有这些都是非常重要的,所以为了清晰起见,我必须删除一些内容。 – Raoot