新手问题 ActiveRecord 的关联,一定要产生那么多的 SQL 吗?

milk · 2013年03月15日 · 最后由 robin 回复于 2013年03月16日 · 2896 次阅读

初使用 AR 的关联,比如我有一个表 book 和 master,每本书有一个主人,我要列出 20 本书,同时列出主人的名字 我就在 book 表里用了belongs_to :master,然后找出 20 条数据,结果在输出时看到了足足 21 条 SQL

到底是不是我用错了,或者我用的时机不对?否则效率未免太低了点吧,要是我要显示 100 本书那不是要 101 条 SQL 了?

麻烦解惑,万分感谢!

关联本身只管定义关系,性能什么的他不会关心。你这是典型的 n + 1 问题,一条 SQL 查出所有 book,20 条 SQL 对每个 book 查出它的 master。 用 include 可以解决你所说的问题。

Book.include(:master).all

生成的 SQL 你自己看看,很好懂的。

通常的想法是查询多导致效率低,但 robbin 最近写的 blog 谈到这个问题,有不一样的见解 http://robbinfan.com/blog/38/orm-cache-sumup

摘录一段:

总结来说,ORM缓存的基本理念是:


以减少数据库服务器磁盘IO为最终目的,而不是减少发送到数据库的SQL条数。实际上使用ORM,会显著增加SQL条数,有时候会成倍增加SQL。


数据库schema设计的取向是尽量设计 细颗粒度 的表,表和表之间用外键关联,颗粒度越细,缓存对象的单位越小,缓存的应用场景越广泛


尽量避免多表关联查询,尽量拆成多个表单独的主键查询,尽量多制造 n + 1 条查询,不要害怕“臭名昭著”的 n + 1 问题,实际上 n + 1 才能有效利用ORM缓存

@suupic 正好昨天也看了,提到性能瓶颈有时出在磁盘 IO,确实挺赞的。

谢谢几位的回答,看了 Robin 的文章,受益良多

不过@darkbaby123 说的方法,我试了下,并不能成立,报错说“include”是私有方法,是我的 Rails 版本问题?

Ruby 1.9.3 Rails 3.2.12

@milk, 应该是includes

@darkbaby123 看了 API,现在我这是样写的 Book.find(:all, :include => :master)

@milk ,用 rails 3 API 会比较好,这样写:

Book.includes(:master)

P.S. 通常情况下我不会用 all,all 会直接读数据库并返回一个 array,而像includeswhere 等 rails 3 API 会等到真正需要读数据的时候才去查询数据库

@leomao10 😄 谢谢,了解了

@milk sorry 记错了,我每次写这个都要纠结下是 include 还是 includes…… @leomao10 嗯,确实。当时写的时候只想给个简单例子,没想这么多。

#8 楼 @leomao10 用 where(true) 呢?我最近也发现 all 不好了,where 这些可以像搭积木一样各种组合,很有趣

#9 楼 @milk 螃蟹,你这个老土怎么还在用 rails2?

@milk @ywencn 你们两有基情?

#12 楼 @ywencn 我这里也是 Rails 2 好土。。

#3 楼 @suupic 这也是我所偏爱的方式,可在维护公司项目常常看到日志里一连四五行长长的 Join 查询,着实让人痛苦不堪。@robin 在这个方向开个好头,非常棒!

此 robin 非彼 robbin...

需要 登录 后方可回复, 如果你还没有账号请 注册新账号