锁定老帖子 主题:Rails sql延迟加载和自带缓存
精华帖 (0) :: 良好帖 (2) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2010-12-30
最后修改:2010-12-31
color_lot_manuallies = color_lot.color_lot_manuallies if color_lot_manuallies.size == 1 end 引用 SELECT count(*) AS count_all FROM `color_lot_manuallies` WHERE (`color_lot_manuallies`.color_lot_id = 237)
当有A has many B时,a.bs.size并不是检索出所有a.bs再求出size,而是直接用select count(*)来计算结果。这应该是延迟加载,有点类似named_scope,但是并不是named_scope,named_scope的结果类是ActiveRecord::NamedScope::Scope,但是A.bs.class的结果是Array,这部分看了下源码但是并没有看懂。 所以当我这样写的时候: color_lot_manuallies = color_lot.color_lot_manuallies if color_lot_manuallies.size == 1 end color_lot.color_lot_manuallies.each do |i| puts i.id end 引用 SQL (0.6ms) SELECT count(*) AS count_all FROM `color_lot_manuallies` WHERE (`color_lot_manuallies`.color_lot_id = 200)
ColorLotManually Load (0.1ms) SELECT * FROM `color_lot_manuallies` WHERE (`color_lot_manuallies`.color_lot_id = 200) 这样的话rails使用了延迟加载造成两个sql不同,所以后面。color_lot.color_lot_manuallies再次使用时,不会使用缓存的sql。 改成这样: color_lot_manuallies = color_lot.color_lot_manuallies.all if color_lot_manuallies.size == 1 end color_lot.color_lot_manuallies.each do |i| puts i.id end 引用 ColorLotManually Load (0.1ms) SELECT * FROM `color_lot_manuallies` WHERE (`color_lot_manuallies`.color_lot_id = 198)
CACHE (0.0ms) SELECT * FROM `color_lot_manuallies` WHERE (`color_lot_manuallies`.color_lot_id = 198) 加一个all,color_lot.color_lot_manuallies.all,这样的话,后面再次调用时,会使用rails的sql缓存。 #PurchaseOrder has_one :purchase_order_marketing, :dependent => :destroy has_many :purchase_invoices, :dependent => :destroy,:through => :purchase_order_marketing #PurchaseOrderMarketing has_many :purchase_invoices,:dependent => :destroy PurchaseOrder.first.purchase_invoices.all(:select => 'purchase_invoices.id') 引用 PurchaseOrder Load (0.2ms) SELECT * FROM `purchase_orders` LIMIT 1
PurchaseInvoice Load (0.8ms) SELECT purchase_invoices.id FROM `purchase_invoices` INNER JOIN `purchase_order_marketings` ON `purchase_invoices`.purchase_order_marketing_id = `purchase_order_marketings`.id WHERE ((`purchase_order_marketings`.purchase_order_id = 13)) 一条sql就完成了,真强大。 #article def sizes sizes = [] art = self sf = art.article_secondary_feature size_group = SizeGroup.find_by_id(sf.size_groupid) if sf sizes = size_group.sizes if size_group return sizes end ArticleMarketing.first.article.sizes.all(:select => 'id') 引用 ArticleMarketing Load (0.3ms) SELECT * FROM `article_marketings` LIMIT 1
Article Load (0.8ms) SELECT * FROM `articles` WHERE (`articles`.`id` = 43) ArticleSecondaryFeature Load (0.7ms) SELECT `article_secondary_features`.* FROM `article_secondary_features` WHERE (`article_secondary_features`.article_id = 43) SizeGroup Load (0.6ms) SELECT * FROM `size_groups` WHERE (`size_groups`.`id` = 2) LIMIT 1 Size Load (1.0ms) SELECT id FROM `sizes` WHERE (`sizes`.size_group_id = 2) find和find_by_id params[:root].classify.constantize.find_by_id(params[:id]) params[:root].classify.constantize.find(params[:id]) 引用 Opportunity Load (0.1ms) SELECT * FROM `opportunities` WHERE (`opportunities`.`id` = '126') LIMIT 1
Opportunity Load (0.1ms) SELECT * FROM `opportunities` WHERE (`opportunities`.`id` = 126) find_by_id的SQL多了一行limit 1,对于sql语句而言,有一点性能上的提高。不过有时候我们需要find来捕捉异常。如果能用find_by_id最好了。 都改成find_by_id 引用 Opportunity Load (0.1ms) SELECT * FROM `opportunities` WHERE (`opportunities`.`id` = '126') LIMIT 1
CACHE (0.0ms) SELECT * FROM `opportunities` WHERE (`opportunities`.`id` = '126') LIMIT 1 这样都有缓存了。rails的自带的缓存是很脆弱的,B.find_by_id(a.id)方法变成a.b时,这个缓存就不会用上。同样前面的例子里改成 color_lot_manuallies = color_lot.color_lot_manuallies.all ColorLotManually.find_all_by_color_lot_id(color_lot.id) rails自带的缓存也不会用上。 关于||=缓存,参考 http://fuliang.iteye.com/blog/827321 http://www.iteye.com/topic/810957 但是有一点,||=不会自动清除或者更新,所以使用的时候还是要注意点,可能会引起取值错误,而且不会报错。 class PortOfDischage < ActiveRecord::Base def _name @_name ||= self.city end end Reloading... => true >> p=PortOfDischage.first SQL (0.2ms) SET SQL_AUTO_IS_NULL=0 PortOfDischage Load (21.2ms) SELECT * FROM `port_of_dischages` LIMIT 1 PortOfDischage Columns (1.7ms) SHOW FIELDS FROM `port_of_dischages` +----+------------+-----------------+---------+---------+--------------------------------+--------------------------------+ | id | ap_list_id | ap_marketing_id | city | country | created_at | updated_at | +----+------------+-----------------+---------+---------+--------------------------------+--------------------------------+ | 1 | 2 | 2 | Piraeus | Greece | Thu Oct 07 07:10:21 +0800 2010 | Thu Oct 07 07:10:21 +0800 2010 | +----+------------+-----------------+---------+---------+--------------------------------+--------------------------------+ 1 row in set >> p._name => "Piraeus" >> p.update_attribute(:city,'p') SQL (0.2ms) BEGIN ApMarketing Columns (43.0ms) SHOW FIELDS FROM `ap_marketings` ApMarketing Load (18.6ms) SELECT * FROM `ap_marketings` WHERE (`ap_marketings`.`id` = 2) PortOfDischage Update (45.5ms) UPDATE `port_of_dischages` SET `updated_at` = '2010-12-30 13:08:47', `city` = 'p' WHERE `id` = 1 SQL (55.5ms) COMMIT => true >> p._name => "Piraeus" >> p.city => "p" >> p.reload PortOfDischage Load (0.7ms) SELECT * FROM `port_of_dischages` WHERE (`port_of_dischages`.`id` = 1) +----+------------+-----------------+------+---------+--------------------------------+--------------------------------+ | id | ap_list_id | ap_marketing_id | city | country | created_at | updated_at | +----+------------+-----------------+------+---------+--------------------------------+--------------------------------+ | 1 | 2 | 2 | p | Greece | Thu Oct 07 07:10:21 +0800 2010 | Thu Dec 30 13:08:47 +0800 2010 | +----+------------+-----------------+------+---------+--------------------------------+--------------------------------+ 1 row in set >> p._name => "Piraeus" 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-01-01
ActiveRecord的has_many关联和scope很相似,都用了delegation,rails 2.3.x中返回的是namedscopes或associations。
虽然返回的是array,但是在对返回的associations或namedscopes调用方法时候,会根据不同的方法,delegate到不同的对象。 调用:scopes, :with_scope, :scoped_methods等时继续返回scope 调用除了[nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?]中的方法时,先调用.all方法进行db操作。 即: user.posts.each{...} == user.posts.all.each{...} Rails3就更变态了,到处是lazy loading. where having select group order limit offset joins includes (:include) lock readonly from 这些方法都仍然返回一个Relation对象。直到调用map/each等方法才真正的进行数据库操作。 引用 CACHE (0.0ms) SELECT * FROM `opportunities` WHERE (`opportunities`.`id` = '126') LIMIT 1
这样的日志是mysql的缓存吧,和rails一点关系没有。 无论find还是find by id都进行了一次db操作。而这样的主键查询有没有limit 1貌似没有差别吧。 find和find by id的最大区别就是抛不抛异常的区别。还有就是find by 会调用method_missing,很多人说这东西慢... 像这样简单的查询,用cache money或其他缓存插件都可以避免hit db的。 |
|
返回顶楼 | |
发表时间:2011-01-01
最后修改:2011-01-01
Hooopo 写道 引用 CACHE (0.0ms) SELECT * FROM `opportunities` WHERE (`opportunities`.`id` = '126') LIMIT 1
这样的日志是mysql的缓存吧,和rails一点关系没有。 无论find还是find by id都进行了一次db操作。而这样的主键查询有没有limit 1貌似没有差别吧。 find和find by id的最大区别就是抛不抛异常的区别。还有就是find by 会调用method_missing,很多人说这东西慢... 像这样简单的查询,用cache money或其他缓存插件都可以避免hit db的。 这个是rails的缓存。mysql的缓存是另外一回事,你搞混了。 加limit 1还是有一点点效果的,你可以在mysql下做下测试。 我标题里已经写了,rails自带缓存。当然,如果用memcache或者其他的插件,这个应该是可以避免hit db的。不知道有没有办法把rails的自带缓存禁止掉。 引用 ActiveRecord的has_many关联和scope很相似,都用了delegation,rails 2.3.x中返回的是namedscopes或associations。 虽然返回的是array,但是在对返回的associations或namedscopes调用方法时候,会根据不同的方法,delegate到不同的对象。 调用:scopes, :with_scope, :scoped_methods等时继续返回scope 调用除了[nil? send object_id class extend find size count sum average maximum minimum paginate first last empty? any? respond_to?]中的方法时,先调用.all方法进行db操作。 即: user.posts.each{...} == user.posts.all.each{...} Rails3就更变态了,到处是lazy loading. where having select group order limit offset joins includes (:include) lock readonly from 这些方法都仍然返回一个Relation对象。直到调用map/each等方法才真正的进行数据库操作。 rails3还没怎么玩。恩,delegate,恩,有时间在看看。 |
|
返回顶楼 | |
发表时间:2011-01-05
引用 这个是rails的缓存。mysql的缓存是另外一回事,你搞混了。
确实搞混了,这个是rails的QueryCache打的日志~~ 这个缓存是action级别的,一个action之内的相同查询不会再去数据库取新的(如果当前action没有更新这条记录)。但弱的很,不过不需要手动设置什么,很方便的。也有一些隐藏的危险,比如: p User.first(:order => "RAND()") p User.first(:order => "RAND()") 这样两次结果是相同的,如果开启了QueryCache。 引用 加limit 1还是有一点点效果的,你可以在mysql下做下测试。
这个效果还真没看出来,可能数据太少了。 引用 不知道有没有办法把rails的自带缓存禁止掉。
User.uncached do p User.first(:order => "RAND()").id p User.first(:order => "RAND()").id end |
|
返回顶楼 | |
发表时间:2011-01-05
最后修改:2011-01-05
(占位)
引用 order => "RAND(
很邪恶。。这个order by rand貌似mysql都不会缓存吧。。 |
|
返回顶楼 | |
发表时间:2011-01-05
这些方法都仍然返回一个Relation对象。直到调用map/each等方法才真正的进行数据库操作。 是对数据进行操作系吧。不然执行的不会那么慢,不知道你说的是3还是2+ |
|
返回顶楼 | |
发表时间:2011-01-05
orcl_zhang 写道 (占位)
引用 order => "RAND(
很邪恶。。这个order by rand貌似mysql都不会缓存吧。。 我理解的mysql缓存是在select和where中间的那个字段或值,不知道正不正确,求解 |
|
返回顶楼 | |
发表时间:2011-01-06
最后修改:2011-01-06
ddl1st 写道 orcl_zhang 写道 (占位)
引用 order => "RAND(
很邪恶。。这个order by rand貌似mysql都不会缓存吧。。 我理解的mysql缓存是在select和where中间的那个字段或值,不知道正不正确,求解 引用 1. 为查询缓存优化你的查询
大多数的MySQL服务器都开启了查询缓存。这是提高性最有效的方法之一,而且这是被MySQL的数据库引擎处理的。当有很多相同的查询被执行了多次的时候,这些查询结果会被放到一个缓存中,这样,后续的相同的查询就不用操作表而直接访问缓存结果了。 这里最主要的问题是,对于程序员来说,这个事情是很容易被忽略的。因为,我们某些查询语句会让MySQL不使用缓存。请看下面的示例: // 查询缓存不开启 $r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()"); // 开启查询缓存 $today = date("Y-m-d"); $r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'"); // 查询缓存不开启 $r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()"); // 开启查询缓存 $today = date("Y-m-d"); $r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'"); 上面两条SQL语句的差别就是 CURDATE() ,MySQL的查询缓存对这个函数不起作用。所以,像 NOW() 和 RAND() 或是其它的诸如此类的SQL函数都不会开启查询缓存,因为这些函数的返回是会不定的易变的。所以,你所需要的就是用一个变量来代替MySQL的函数,从而开启缓存。 引用 6. 千万不要 ORDER BY RAND()
想打乱返回的数据行?随机挑一个数据?真不知道谁发明了这种用法,但很多新手很喜欢这样用。但你确不了解这样做有多么可怕的性能问题。 如果你真的想把返回的数据行打乱了,你有N种方法可以达到这个目的。这样使用只让你的数据库的性能呈指数级的下降。这里的问题是:MySQL会不得不去执行RAND()函数(很耗CPU时间),而且这是为了每一行记录去记行,然后再对其排序。就算是你用了Limit 1也无济于事(因为要排序) 下面的示例是随机挑一条记录 // 千万不要这样做: $r = mysql_query("SELECT username FROM user ORDER BY RAND() LIMIT 1"); // 这要会更好: $r = mysql_query("SELECT count(*) FROM user"); $d = mysql_fetch_row($r); $rand = mt_rand(0,$d[0] - 1); $r = mysql_query("SELECT username FROM user LIMIT $rand, 1"); http://coolshell.cn/articles/1846.html#more-1846 |
|
返回顶楼 | |
发表时间:2011-01-07
都是需要驱动的:
返回的是namedscopes或associations,因为有时候我们的目的并非是查询,还可能会创建或其他操作,比如current_user.posts.new,这个时候current_user.posts就不能直接查询返回数组了。 find 和 find_by也是根据需要场景来决定用谁。个人觉得比较他们的性能并没有意义。 find_by == find(:first),是find的特别版,find是不是抛异常和它返回是数组还是别的,要看find的第一个参数是什么。 如果你需要判断一个记录是否存在并返回它,就用find_by,如果仅仅是批量查询就用find |
|
返回顶楼 | |
发表时间:2011-01-07
最后修改:2011-01-07
K-PAX 写道 find 和 find_by也是根据需要场景来决定用谁。个人觉得比较他们的性能并没有意义。 http://coolshell.cn/articles/1846.html#more-1846 比较性能,多数情况下确实没太大意义。。 不过因为项目里面有类似于exist?(id),然后find(id),如此多此一举。 而且也只有这个表记录在20多万,数据量稍微多点,查询相当频繁,基本上每个action会查几十次。就这种情况下做了下修改,都改成了find_by. |
|
返回顶楼 | |