如何使低级缓存与Rails中的关联缓存协作? [英] How to make Low-Level caching collaborate with Association caching in Rails?
问题描述
我目前正在使用Rails 5进行项目.我想提高性能,所以我决定像下面这样使用低级缓存:
class用户<应用记录has_one:个人资料def cached_profileRails.cache.fetch(['Users',id,'profile',Updated_at.to_i])做轮廓结尾结尾结尾类配置文件<应用记录属地:用户,触摸:真结尾
它分别工作正常.但是现在我想使两个缓存相互协作.我想要的是,一旦从缓存存储区中检索了关联的对象,就不需要从数据库中检索它了.
irb>u = User.takeirb>u.cached_profile#从redis获取.我可以在`cached_profile`中设置关联缓存吗?irb>u.profile#从数据库中获取配置文件加载(1.4ms)在配置文件"中从配置文件"中选择配置文件"."user_id" = $ 1 LIMIT $ 2 [["id",1],["LIMIT",1]]irb>u.profile#从关联缓存中获取
u.profile
不应该从数据库中获取,因为它已经从redis中获取了,如何实现呢?
更新1:
我发现在 ActiveRecord :: Base
的实例中有一个实例变量 @association_cache
,该变量存储缓存的关联并确定是否应从数据库中检索关联
我想我可以做类似 user.instance_variable_get(:@ association_cache)['profile'] = cached_profile
的事情来使其工作.但是 @association_cache
中的值是 ActiveRecord :: Associations :: HasOneAssociation
的实例,我不知道如何构建 user
当前.
ActiveRecord :: Base实例中有一个实例变量@association_cache,用于存储缓存的关联并确定是否应从数据库中检索关联.>
我们可以使用 @association_cache
来实现它,例如:
class用户<应用记录has_one:个人资料def cached_profile缓存= Rails.cache.fetch(['Users',id,'profile',Updated_at.to_i])做轮廓结尾反射= self.class.reflect_on_association(:profile)如果association_instance_get(name).nil?关联= Reflection.association_class.new(自我,反射)association.target =缓存association_instance_set(:profile,关联)结尾快取结尾结尾类配置文件<应用记录属地:用户,触摸:真结尾
现在,如果已经从缓存存储中检索到数据,我们可以避免从数据库中重新获取数据.
irb>u = User.takeirb>u.cached_profile#从数据库中获取配置文件并使用redis对其进行缓存配置文件加载(1.4ms)在配置文件"中从配置文件"中选择配置文件"."user_id" = $ 1 LIMIT $ 2 [["id",1],["LIMIT",1]]irb>u.profile#使用redis的缓存版本irb>u.profile.reload#从数据库重新加载配置文件加载(1.4ms)在配置文件"中从配置文件"中选择配置文件"."user_id" = $ 1 LIMIT $ 2 [["id",1],["LIMIT",1]]
更新:
为此,我构建了一个 cache_associations .
class用户<应用记录包括CacheAssociationshas_one:个人资料cache_association:profile结尾
它在做同样的事情,但是它整洁并且易于使用.
I am currently working on a project with Rails 5. I want to boost the performance, so I decided to use Low-Level caching like the following:
class User < ApplicationRecord
has_one :profile
def cached_profile
Rails.cache.fetch(['Users', id, 'profile', updated_at.to_i]) do
profile
end
end
end
class Profile < ApplicationRecord
belongs_to :user, touch: true
end
It works fine respectively. But now I want to make the two caches to collaborate. What I want is that the associated object doesn't need to be retrieved from the database once it is retrieved from the cache store(redis here).
irb> u = User.take
irb> u.cached_profile # fetch from the redis. Can I set the association caching in `cached_profile`?
irb> u.profile # fetch from the database
Profile Load (1.4ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."user_id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
irb> u.profile # fetch from the association caching
u.profile
shouldn't fetch from the database because of it's already retrived from the redis, how to achieve this?
Update 1:
I found that there's an instance variable @association_cache
in the instance of ActiveRecord::Base
, which stores cached associations and determines if an association should be retrieved from the database.
I think I could do something like user.instance_variable_get(:@association_cache)['profile'] = cached_profile
to make it work. But the value in the @association_cache
is an instance of ActiveRecord::Associations::HasOneAssociation
and I don't know how to build the user
to it currently.
There's an instance variable @association_cache in the instance of ActiveRecord::Base, which stores cached associations and determines if an association should be retrieved from the database.
We can achieve it with @association_cache
like:
class User < ApplicationRecord
has_one :profile
def cached_profile
cache = Rails.cache.fetch(['Users', id, 'profile', updated_at.to_i]) do
profile
end
reflection = self.class.reflect_on_association(:profile)
if association_instance_get(name).nil?
association = reflection.association_class.new(self, reflection)
association.target = cache
association_instance_set(:profile, association)
end
cache
end
end
class Profile < ApplicationRecord
belongs_to :user, touch: true
end
Now we can avoid to refetch data from the database if it has been retrieved from the cache store.
irb> u = User.take
irb> u.cached_profile # fetch the profile from the database and use the redis to cache it
Profile Load (1.4ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."user_id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
irb> u.profile # use the cached version from the redis
irb> u.profile.reload # reload from the database
Profile Load (1.4ms) SELECT "profiles".* FROM "profiles" WHERE "profiles"."user_id" = $1 LIMIT $2 [["id", 1], ["LIMIT", 1]]
UPDATE:
I built a gem cache_associations for this.
class User < ApplicationRecord
include CacheAssociations
has_one :profile
cache_association :profile
end
It's doing the same thing, but it's neat and much easier to use.
这篇关于如何使低级缓存与Rails中的关联缓存协作?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!