数据库是瓶颈,今天我们介绍model级别的cache。Cached Model 是一个简单的只对单记录做缓存的plugin.Cached Model的的存储分为本地存储和Memcached存储。本地存储大家都知道,一般都是用Hash来存储的。这里的Memcached是一种网络分布式存储。
我们先来看看如果应用cached model
一:安装
gem install cached_model -y
二:应用
简单的创建一个工程
php?name=rails" onclick="tagshow(event)" class="t_tag">rails cached_model_demo
新建Page
ruby script/generate scaffold_resource page title:string content:text created_at:timestamp
创建数据库
修改Page Model,继承CachedModel
class Page < CachedModel
end
在enviroment中增加memcached的配置的代码:
require 'cached_model'
memcache_options = {
:c_threshold => 10_000,
:compression => true,
:debug => false,
:namespace => 'cached_model_demo',
:readonly => false,
:urlencode => false
}
CACHE = MemCache.new memcache_options
CACHE.servers = 'localhost:11211'上面都是一些简单的东西,我们就忽略了,有什么问题,大家提出来。
好了,准备就绪。cached_model 默认是采用memcached store的方式,当然你可以采用local的方式
CachedModel::use_local_cache=true
启动memcached server
我们传递vv参数可以查看更多的日志信息,具体的可以查看--help
打开ruby script/console 并打开tail -f log/development.log查看日志
新建page
Page.create :title=>'Hello World'
Page.create :title=>'Hello World Again'
成功。cached_model只覆盖了ActiveRecord的两个方法,一个是find,一个是find_by_sql.我们具体的分析一下这两个方法
1:) find
def self.find(*args)
args[0] = args.first.to_i if args.first =~ /\A\d+\Z/
# Only handle simple find requests. If the request was more complicated,
# let the base class handle it, but store the retrieved records in the
# local cache in case we need them later.
if args.length != 1 || !Fixnum === args.first then
# Rails requires multiple levels of indirection to look up a record
# First call super
records = super
# Then, if it was a :all, just return
return records if args.first == :all
return records if RAILS_ENV == 'test'
case records
when Array then
records.each { |r| r.cache_store }
end
return records
end
return super
end这里面压根没有读取cache的代码,不管这么样,都会先调用super执行查询,只有在
>参数个数不为1或第一个不是数字
>第一个参数不是:all
>不是test环境下
>返回结果为Array的时候才缓存model
也就是说基本没什么作用。
我们试验下
依然执行SQL,memcached无反应
我们会看到memcached中有了两条记录
<1924 new client connection
<1924 set cached_model_demo:active_record:Page:1 0 900 95
>1924 STORED
<1924 set cached_model_demo:active_record:Page:2 0 900 101
>1924 STORED
保存了两条记录
在执行
依然查询数据库。那我们该如何取这样的数据呢?
>> Cache.get "active_record:Page:1"
=> #"Hello World", "id"=>"1", "content"=>nil, "cr
ed_at"=>"2007-11-10 18:29:29"}>
2:)find_by_sql
def self.find_by_sql(*args)
return super unless args.first =~ /^SELECT \* FROM #{table_name} WHERE \(?#{table_name}\.#{primary_key} = '?(\d+)'?\)? +LIMIT 1/很简单,但是他只cache根据主键id查询的record
Page.find_by_sql "SELECT * FROM PAGES WHERE PAGES.ID = 1 LIMIT 1"
<1924 get cached_model_demo:active_record:Page:1
>1924 sending key cached_model_demo:active_record:Page:1
>1924 END
development.log无SQL查询
MemCache Get (0.000000) active_record:Page:1
这里取回的是我们刚才执行Page.find 1,2,缓存1的数据,如果没有,则会存储该数据,下次再查询直接从cache中获取。
三:更新
当该id的记录update,destroy都会更新cache,这个没什么好说的。
四:改进
这些功能太过于简单了,我们可以稍微对源码做点修改
一:缓存Page.find 的单条记录
if CachedModel.use_memcache?
record = Cache.get "active_record:#{name}:#{args.first}"
#return cached model
return record unless record.nil?
#call super find
record = super
#store in memcache
record.cache_store
return record
end
end二:find_by_sql的改进,空格,大小写必须一致才能生效
这条正则表达式太过于简单,我们可以稍微修改
1:) 忽略大小写 /i
2:) 忽略table的名称,只需要id=?几匹配
2;) 忽略空格
警告:
对于有关联的Model请谨慎使用cached_model,建议不使用。
[
本帖最后由 martin 于 2007-11-12 09:38 编辑 ]