Ruby/Rails 数据库查询低级缓存实现问题求助
解决Rails首页24小时低级缓存仅一次数据库请求的问题
我明白你想实现的需求:让首页未过滤的情况下每天只触发一次数据库查询,用低级缓存来达成这个目标。咱们一步步拆解你代码里的问题,然后给出可行的解决方案。
先说说你之前代码里的核心问题
第一个版本代码的问题
你最初尝试把Freelancer.order(featured: :desc)这个ActiveRecord关系缓存起来,但ActiveRecord关系并不是实际的数据,它只是查询语句的构建器。缓存关系对象不会执行数据库查询,每次你使用这个关系时,还是会触发新的数据库请求,完全达不到缓存的目的。另外,你的index方法没有接收freelancers_scope参数,导致freelancers_scope始终是nil,会无限递归调用cache方法,这肯定会报错。
更新后代码的问题
你改成缓存哈希数组的思路是对的,但细节上有问题:
- 用
map(&:attributes)把模型对象转成哈希,会丢失模型的方法和关联,后续如果需要调用模型的业务逻辑会很麻烦; - 过滤逻辑里的条件搞反了,而且
params[:cost_greater_than].presence如果为nil的话,会生成无效的范围(比如(0..nil)),直接导致报错; - 没有处理参数类型转换,字符串和数字比较可能会出问题。
正确的实现方式
下面是修正后的代码,每一步我都加了注释说明:
class HomeController < ApplicationController before_action :set_freelancer, only: %i[ show edit update destroy ] def index # 缓存原始的Freelancer列表,24小时过期 # 使用.to_a获取实际的记录数组,而不是ActiveRecord关系,这样缓存的是真实数据 base_freelancers = Rails.cache.fetch('freelancers', expires_in: 24.hours) do Freelancer.order(featured: :desc).to_a end # 复制原始数组,避免修改缓存中的数据 filtered_freelancers = base_freelancers.dup # 处理「最低成本」过滤:cost >= 传入的最小值 if params[:cost_greater_than].present? min_cost = params[:cost_greater_than].to_i filtered_freelancers = filtered_freelancers.select { |freelancer| freelancer.cost >= min_cost } end # 处理「最高成本」过滤:cost <= 传入的最大值 if params[:cost_lower_than].present? max_cost = params[:cost_lower_than].to_i filtered_freelancers = filtered_freelancers.select { |freelancer| freelancer.cost <= max_cost } end # 用pagy_array处理数组分页,和你之前的用法一致 @pagy, @freelancers = pagy_array(filtered_freelancers) end private def set_freelancer @freelancer = Freelancer.find(params[:id]) end end
关键要点说明
- 缓存实际数据而非查询关系:用
.to_a让ActiveRecord立即执行查询并返回记录数组,这样缓存的是真实的数据集,24小时内不会再触发数据库查询。 - 正确处理过滤逻辑:修正了参数和条件的对应关系,同时做了类型转换,避免字符串和数字比较的问题;复制数组是为了保证缓存的原始数据不被修改,确保后续请求的缓存一致性。
- 保留模型对象:直接缓存模型实例数组,而不是哈希,这样后续如果需要调用模型的方法(比如
freelancer.full_name)依然可以正常使用。
这样修改后,就能实现你想要的:未过滤的首页每天仅发起一次数据库请求,过滤请求则基于缓存的数据在内存中处理,完全符合你的需求。
内容的提问来源于stack exchange,提问作者Luke Czoboriek
相关产品推荐
相关产品推荐

