数据缓存提升性能gem - Dalli介绍

Rails可能很多人诟病的就是性能问题,所以很多人想出来了改善方法,其中dalli这个gem就是通过memcache来改善的。

地址

https://github.com/mperham/dalli 使用这个gem必须保证安装了memcached

什么数据适合存入cache呢?

一般情况下,数据库里面不怎么改变的数据,并且使用的地方比较多的话是非常适合存入cache的。例如:一个国家的所有省份。

举个例子说明下:

1
2
3
4
5
6
7
class Grade < ActiveRecord::Base
  has_many :skills
end

class Skills < ActiveRecord::Base
  belongs_to :grade
end

如果想取得一个grade所有skills非常简单如下:

1
2
grade = Grade.first
skills = grade.skills

现在每次取一个grade的skills都会访问数据库,可以考虑把skill放到cache里面去了。

1
2
3
4
5
def cached_skills
  Rails.cache.fetch([self.class.name, id, :skills], expires_in: 240.hours) {
    skills.to_a
  }
end

所以我们取skills的话可以通过下面方式取出来

1
grade.cached_skills

这个过程是什么样的呢?当第一次访问的时候,会去访问数据库然后把数据存入到memcached里面去,下次访问的话就直接从cache里面取了,不再访问数据库了。

  • [self.class.name, id, :skills] 这是存储的key
  • cache将会在240小时后过期
  • 如果不转化成数组(to_a)直接将active-record的relations存储到cache的话,每次访问cache这个relations还是会查询数据库的,所以大家务必要注意。

让cache过期

如果某个grade的skills改变了,而grade又没有收到通知,这时候从cache中取出来的将是旧的数据,这个时候我们需要让cache里面这个grade过期或者删除掉

一种方法使用after_commit

在Skill的model里面加上

1
2
3
4
5
6
# in skill model
after_commit :flush_cache

def flush_cache
  Rails.cache.delete([self.grade.class.name, self.grade_id, :skills])
end

这个方法可以保证cache里面的数据都是最新的。

第二种方法是通过改变key和加入touch关键字

model定义如下:

1
2
3
4
5
6
7
class Grade < ActiveRecord::Base
  has_many :skills
end

class Skills < ActiveRecord::Base
  belongs_to :grade, touch: true
end

touch什么意思呢?如果skill更新的话,也会更新grade的updated_at字段

这时候将cached_skills方法修改成下面的:

1
2
3
4
5
def cached_skills
  Rails.cache.fetch([self.class.name, updated_at.to_i, :skills], expires_in: 240.hours) {
    skills.to_a
  }
end

这样的话如果skill发生改变的话,因为把updated_at作为key了,所以从cache里面取的永远是最新的skills。

但这种方法有如下缺点,使用的时候需要小心:

在这种情况下,如果topic改变也会更新grade的updated_at,这样的话,也会影响skills,cache的效果就不明显了。 上面的2种cache过期方法,大家根据实际情况自行选择。

总结

这个gem可以帮助我们减少很多数据库访问,极大的提高性能。大家使用的时候,务必注意cache里面数据保持更新的问题,不然引起很多因为旧数据引起的bug就不太好了。

评论