Rails关于Fragment Cache 复杂缓存,DB层控制,实例见解

最近项目中遇到了performance问题,页面刷新,loading时间很长,需要优化的问题。期间查询了各种资料,准备用cache来解决这个问题,其中感谢一个博主的思路《Rails缓存套娃机制》,让我深受启发。我的大牛leader用了一种全新的方式解决了这个大难题,我用自己理解的方式,整下如下。

问题环境:页面引用数据量比较大,引用的表也非常多,1对多,1对1。

关于Fragment Cache的相关概念请自行查找官网解释。我的简单理解就是,生成唯一key,保存缓存片段,每次load,当key不一样的时候,重新缓存,当相同的时候,获取缓存 

在需要缓存的页面地方加入cache

      %tbody
        - line_index = 0
        - @students.each do |student|
          - cache @cache_ids_hash[student.id] do
            %tr
              %td
                = line_index.to_s
              %td
	        = student.id

在cache方法内部,会把对象进行一番处理,最后生成一个唯一字符串,views/students/xxxxxxxxx,或者其他字符串

这里重点介绍如何利用这个唯一字符串来解决缓存问题。

问题环境:students表,classes表,parents表,teachers表,scores表

后面几张表都于student表有关联,需要将这几张表中的数据,挑选一些数据显示在一个view里。

这里设计的模式是在页面层次一次cache住所有数据,在help里处理自主生成独立的cahce key,然后在DB层加入trigger,更新主表student,实现缓存 


具体分布说明

       1.页面加入cache,如上述所述

       2.主表students加入唯一标识符column,命名为cache_trigger_timestamp, timestamp类型,主要为生成唯一编号做准备

       3.增加trigger,关联唯一标识符cache_trigger_timestamp,当监听的表有数据变动的时候,更新主表的cache_trigger_timestamp字段

       这样做的好处:

            对classes表,parents表,teachers表,scores表做trigger,当发生变化的时候, 更新student主表的cache_trigger_timestamp字段,在数据层,就很会容易判断是否有新的cache,是否需要缓存

      具体trigger(举例一个)    

DROP TRIGGER IF EXISTS 'teachers_cache_timestamps_update'
DROP TRIGGER IF EXISTS 'teachers_cache_timestamps_insert'
DROP TRIGGER IF EXISTS 'teachers_cache_timestamps_delete'

DELIMITER $$
CREATE TRIGGER 'teachers_cache_timestamps_update' AFTER UPDATE ON 'teachers'
FOR EACH ROW BEGIN
    UPDATE students SET cache_trigger_timestamp = NOW() WHERE teacher_id = NEW.id
END$$

CREATE TRIGGER `teachers_cache_timestamps_insert` AFTER INSERT ON `teachers` 
FOR EACH ROW BEGIN
    UPDATE students SET cache_trigger_timestamp = NOW() WHERE teacher_id = NEW.id
END$$

CREATE TRIGGER `teachers_cache_timestamps_delete` AFTER DELETE ON `teachers` 
FOR EACH ROW BEGIN
    UPDATE students SET cache_trigger_timestamp = NOW() WHERE teacher_id = OLD.id
END$$
DELIMITER ;

       4.建立保存Fragment cache 相关数据的表 ,命名为fragment_caches,

        保存一些有用的数据,如item_type(数据类型,如:student),item_id(数据id),user_id(登录用户id),cache_id(缓存编号),等等需要的字段

        问题点:这些字段有什么用?  

        答:为了扩展需要,比如item_type,区分缓存的是student数据,还是学校数据,或者是其他表数据 

              item_id,目标主要缓存数据的唯一编号id

              user_id为了区分哪个用户的缓存数据,对每个登录用户数据分别缓存

              总结,主要就是为了区分缓存数据,不至于混淆,至于怎么设计,具体问题具体分析

      5.重点来了,controller层,为每条数据生成唯一的cache,并且设计逻辑,判断cache是否相同,是否需要重新cache

        cache_id如何设计,需要应用上各个独特的字符,比如user_id,item_id,item_type,student.updated_at,        student.cache_trigger_timestamp等,中间使用"_"关联,生成一个辨识度高的字符串

     具体生成cache_key逻辑:  每条数据都生成唯一的编号,并返回。

def build_uniq_cache_key(students, item_type, user_id)
    stu_cache_key_hash = Hash.new
    if !students.blank?
      for stu in students
        stu_cache_id = ""
        stu_cache_id << item_type
        stu_cache_id << "_"
        stu_cache_id << user_id.to_s
        stu_cache_id << "_"
        stu_cache_id << students.id.to_s
        stu_cache_id << "_"
        stu_cache_id << stu.id.to_s
        stu_cache_id << "_"
        stu_cache_id << stu.updated_at.strftime("%Y%m%d%H%M%S")
        stu_cache_id << "_"
        stu_cache_id << (!stu.cache_trigger_timestamp.blank? ? stu.cache_trigger_timestamp.strftime("%Y%m%d%H%M%S") : "00000000000000")

        stu_cache_key_hash[stu.id] = stu_cache_id
      end
    end
    return stu_cache_key_hash
  end
       刷新并判断是否生成新的cache

       

 def feature_fragment_caches_refresh(item_type, students, cache_ids_hash, user_id)
    stu_ids = cache_ids_hash.keys
    if !stu_ids.blank?
      cached_stu_ids = []
      fragment_caches = FragmentCache.where("item_type = ? and item_id in (?) and user_id = ? ", item_type, stu_ids, user_id)
      for fragment_cache in fragment_caches
        cached_stu_ids.push(fragment_cache.item_id)
        current_cache_id = cache_ids_hash[fragment_cache.item_id]
        if current_cache_id != fragment_cache.cache_id 
          ActionController::Base.new.expire_fragment(fragment_cache.cache_id)
          if Rails.cache.fetch("views/#{fragment_cache.cache_id}").nil?
            fragment_cache.cache_id = current_cache_id
            fragment_cache.save
          end
        end
      end
      new_save_cache_stu_ids = stu_ids - cached_stu_ids
      new_save_cache_stu_ids.uniq!
      if !new_save_cache_stu_ids.blank?
        for new_save_cache_stu_id in new_save_cache_stu_ids
          new_cache_id = cache_ids_hash[new_save_cache_stu_id]
          new_cache = FragmentCache.new
          new_cache.item_type = "student"
          new_cache.item_id = new_save_cache_stu_id
          new_cache.cache_id = new_cache_id
          new_cache.user_id = user_id
          new_cache.save
        end
      end
    end
  end

 上述的两个方法需要在def show 里调用,完成cache操作

总结,整个cache思想点在于

1.新建cache_trigger_timestamp,其他表的变化,更新此数据,来控制多表的缓存更新

2.生成唯一有特点的cache_id标识符,辨识是否需要更新缓存

3.逻辑判断cache_id,控制缓存


以上是我的总结想法,或许还有些不成熟,不妥之处,请指正,谢谢。

name: eric

email:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值