• 2008-09-03

    缓存出了错

    版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
    http://www.blogbus.com/dreamhead-logs/28508727.html

    最近,一直在“优化”打交道,为的就是尽力榨取计算机的能力。这两天做了一个优化,为我们的应用一个页面加上了缓存,从后台时间来看,效果是非常明显,原来的一个需要十几秒才能处理完的请求,一秒左右就可以返回了。高兴不过片刻,负责测试的同志就站了出来,显然,有问题了。

    原来这个页面上的记录都有一个字段,就是生效日期。这个生效日期可能是未来的某个时间,所以,需要标识出来,而一旦过了这个生效日期,这个字段就无需再显示。如果这个页面是每次都生成,那每次处理请求的时候,就会根据当前日期判断生效日期是否需要显示出来。而一旦这个页面缓存下来,这个标识也就存留在页面上了。

    首先闯入脑子的想法当然是清空缓存。不过,我们清空缓存的条件是每次存储记录时候,而生效日期其实并没有改变,换句话说,它并没有带来记录的改变,清空缓存的条件不存在,所以,依赖存储记录清空缓存的想法是不可行的。

    再有一个想法就是采用鸵鸟算法:置之不理。理由吗?只要新的记录保存,我们就会清理缓存,而且,发布的频率还不算太低。不过,这种解决方案总是让人不安,自己都觉得很不负责任,而且,要给BA和QA以及客户大人们去解释这个理由,需要浪费很多口舌。

    既然最直观的想法不起作用,那就得仔细想想还有什么办法。

    之所以产生这个问题,主要是因为生效日期的标识是在运行时判断的,而并非固定在页面上的。我们是不是需要为了它取消页面缓存呢?显然,为了这个问题,取消页面缓存,性能损失会更大,如果是这样,我宁愿鸵鸟了。

    我们不难发现一个切入点,这是一个生效日期。对,只是生效日期,而不是时间,所以,我们只要可以每天产生一个新页面的缓存,这个问题也就随之得到解决。每天做一次,听起来用这非常适合做一个定时任务。而且系统里面已经有了一些定时任务,再加一个新的,也没有什么大不了的。

    好吧!我承认我很懒,一想到要去写一个定时任务,我就会觉得麻烦,更重要的是,我真的不喜欢后台任务,因为开发和维护都要花很多精力的。通常遇到这种情况,我会问自己一个问题,我们真的需要后台任务吗?有没有其它的方案可以解决这个问题。

    这是一个和时间有关的缓存,我突然想到Rails的那本书上有关于和时间相关缓存的讨论。让我失望的是,那本书上并没有给我什么很实际的回答。于是我开始自己的搜索之旅,我也确实找到了一些基于时间缓存的插件。不过,看了一下用法,我就发现了这些方案并不是很适合我们的问题,因为它们大多是判断缓存是在多长时间内生成的。如果我把时间设成一天,就可能出现第一天的很晚生成的缓存,在第二天大部分时间存在,而第二天的缓存迟迟没有机会生成,所以,页面可能会错一天。

    我们有一个懂技术的BA,就是他发现这个问题的,这时,他又站出来给了我们一个建议:取缓存文件的时间,如果是前几天的,就重新生成。这是一个很好的思路,这样,生成的时机刚好可以吻合业务的需求:每天生成。保证了页面正确性的同时,听起来又不要额外做太多工作。

    不过,真正到了要实现这个想法时,我又打了退堂鼓。用过Rails缓存的人都知道,我们只会配置一个缓存的名字作为键值,其它的一切是交由Rails管理。要我们知道缓存文件的信息,就需要穿透Rails的重重迷雾,找到文件所在。更重要的是,Rails的缓存策略是可以改变的,比如说,可以考虑采用memcache作为缓存,这种做法只会把我们绑死在文件系统上,丧失回旋余地。我们需要继续前行,不过,这条路可以当作最后的解决方案。

    突然,我想起了缓存的名字!

    是的,缓存的名字作为键值,如果我把时间戳加到缓存的名字上呢?其实,我们真正需要的,只是一个时间戳。前面的那个想法,真正要找的是时间戳,所以,如果我们可以找到一个地方存储时间戳,那便万事大吉了。缓存的名字真的是一个不错的选择!

    我看了一下关于缓存名字的实现,我们有一个缓存名字实现的辅助方法,我只要在在这里面加上当前日期,所有的代码相关的代码都可以获益。思路到这里,就只剩一个问题了,缓存的清空,用以清空缓存的方法是一个用正则表达式匹配相关的名字,而我加上日期只是加了一个后缀而已,依然在这个正则表达式的匹配之列。这样看来,这个思路是通的。

    还有什么问题吗?有!因为每一天都会生成新的缓存,而如果不加额外处理的话,旧的缓存是不会得到清理的。那缓存会一直堆积下去?那倒不会,只要有新的记录保存,负责清理缓存的代码就会匹配到所有的缓存,将其清空。由此,看来缓存是会得到清理的,当然,清理的频率取决于记录保存的频率。对比一下,这个方案的投入和付出,这是可以接受的。

    最终,我只改动了一行代码,加上了那个时间戳。
    分享到:

    历史上的今天:

    敏捷中国2011 2011-09-03
    引用地址:

    评论

  • 我只改动了一行代码,加上了那个时间戳。
    ===
    吹牛吧。理论上看,至少也要两行代码:
    1.加时间戳
    2.判断时间戳
    ====
    回复jruby说:
    呵呵,没有什么吹牛的,我就是这么做的。仔细想想,根本不需要额外判断时间戳的代码。
    2008-09-29 11:15:14
  • 不错!
  • 很讨巧的一个方案。

    思索过程很精彩!