现在的位置: 首页 > 综合 > 正文

Rails宝典之第二十三式: counter cache

2013年04月26日 ⁄ 综合 ⁄ 共 2079字 ⁄ 字号 评论关闭


这次就是讲用_count字段来缓存has_many的计数 


看Project和Task的例子: 

Java代码  收藏代码
  1. <h1>Projects</h1>  
  2.   
  3. <table>  
  4. <% for project in @projects %>  
  5.   <tr>  
  6.     <td><%= link_to project.name, poject_path(project) %></td>  
  7.     <td><small>(<%= pluralize project.tasks.size, 'task' %>)</small></td>  
  8.   </tr>  
  9. <% end %>  
  10. </table>  

上面的页面代码对所有的@projects显示tasks.size,看下log: 

Java代码  收藏代码
  1. SQL (0.006385)  SELECT count(*) AS count_all FROM tasks WHERE (tasks.project_id = 326)  
  2. SQL (0.000220)  SELECT count(*) AS count_all FROM tasks WHERE (tasks.project_id = 327)  
  3. SQL (0.000383)  SELECT count(*) AS count_all FROM tasks WHERE (tasks.project_id = 328)  
  4. SQL (0.000197)  SELECT count(*) AS count_all FROM tasks WHERE (tasks.project_id = 329)  
  5. SQL (0.000215)  SELECT count(*) AS count_all FROM tasks WHERE (tasks.project_id = 330)  

上面显示了对每个project都使用SQL来count tasks,我们采用eager loading看看能否改进性能: 

Java代码  收藏代码
  1. class ProjectsController < ApplicationController  
  2.   def index  
  3.     @projects = Project.find(:all, :include => :tasks)  
  4.   end  
  5. end  

再来看看log: 

Java代码  收藏代码
  1. Project Lood Incluing Associations (0.000954)  SELECT projects.'id' AS t0_r0, projects.'name' AS t0_r1, tasks.'id'  
  2. AS t1_r0, tasks.'name' AS t1_r1, tasks.'project_id' AS t1_r2 FROM projects LEFT OUTER JOIN tasks ON tasks.project  
  3. _id = projects.id  

我们看到,使用eager loading确实只用一条SQL语句就完成工作,但是缺点是把tasks表所有的字段信息都取出来了,很多信息是 
没有用的。 

我们来看看更好的解决方案: 

Java代码  收藏代码
  1. ruby script/generate migration add_tasks_count  

我们新建一个migration,给projects表添加一个叫tasks_count的列: 

Java代码  收藏代码
  1. class AddTasksCount < ActiveRecord::Migration  
  2.   def self.up  
  3.     add_column :projects, :tasks_count, :integer, :default => 0  
  4.   
  5.     Project.reset_column_information  
  6.     Project.find(:all).each do |p|  
  7.       p.update_attribute :tasks_count, p.tasks.length  
  8.     end  
  9.   end  
  10.   
  11.   def self.down  
  12.     remove_column :projects, :tasks_count  
  13.   end  
  14. end  

我们还需要告诉Task类开启counter cache: 

Java代码  收藏代码
  1. class Task < ActiveRecord::Base  
  2.   belongs_to :projects, :counter_cache => true  
  3. end  

好了,我们把ProjectsController的index方法改回lazy loading,刷新页面,再看看log: 

Java代码  收藏代码
  1. Project Lood (0.000295)  SELECT * FROM projects  

可以看出,现在数据库只查询projects表的所有字段,查询时间也比前面的方案都短,性能大大提升。

抱歉!评论已关闭.