oscache之刷新缓存flushEntry的使用

标签: oscache 缓存 flushentry | 发表时间:2015-09-19 15:12 | 作者:
出处:http://www.iteye.com

==========================  困扰和痛苦多时的oscache刷新缓存start  =======================

Everyday都不同于2015-9-19 周六15:30 

 

【前言】一般而言,oscache缓存常用于在高并发的情形下。当你初次调用缓存的方法时,如果缓存中还没有响应的key,则会去执行底层的sql语句,并把结果缓存起来。等到【下次】你再调用缓存的方法时,如果传入的是与【这次】”同样“的key(当然不同样的key是会去查库的,并把此次不一样的key对应的查询结果同样地给缓存起来,以此类推。),则会去直接从缓存中取出【这次】缓存好的结果。而不必再去连接数据库执行sql语句了。减少与数据库的交互是oscache缓存的一大作用,频繁的连接数据库但查询的结果都一样可不是什么好事儿。

 

上述的缓存原理大致的可表示为(涉及oschche jar包,需先导入)

import com.opensymphony.oscache.general.GeneralCacheAdministrator; 

//…………………………………………………………………………………………
GeneralCacheAdministrator oscache;

//通常由spring注入
public void setOscache(GeneralCacheAdministrator oscache)
  {
    this.oscache = oscache;
  }
//定义一个从缓存里根据key取缓存结果的方法

//…………………………………………………………
//如果直接传入的key是null或空的,直接返回null;

//…………………………………………………………
    
//先尝试从缓存里取,取到了直接从缓存拿结果返回即可;

//…………………………………………………………………………

//取不到则从库里取,如果从库里取到了结果,则把结果缓存起来,再把结果返回
  
//………………………………………………………………………………
    
  }

 其中,取缓存的语法是:

Object  obj = osCache.getFromCache(key)

保存结果到缓存的语法是:

oscache.putInCache(key, obj);

 

到这儿,我们会一般认为缓存算是真正用起来了。但是,往往容易忽视一个比较重要的东西,那就是——刷新缓存。

从缓存里取东西对应于”查“,但我们谁也不能保证缓存中某个key对应的数据永远是不变的。比如我们从缓存里取机器的信息。但万一某天增加了机器,或修改或删除了机器呢?我们如果还是直接从缓存里面拿原先的数据,则会跟现在最新的数据不同步,造成”滞后“的错误。这是很严重的!

所以除了查缓存,保存结果到缓存之外,我们也不能忽视:” 刷新缓存“。

 

那我们应该怎么办呢?反编译oscache的jar包就会发现它提供了刷新缓存的语法:

oscache.flushEntry(key);

看起来简单吧,但事实上你key如果处理的不好,仍然很容易出错!

 

就拿刚才举的关于机器的例子来讲,假设你在db里面有1张对应于机器信息的表。你把查询结果放在一个list中,并用一个常量例如"servers"来作为缓存中的保存list的key。没问题,你可以直接用上述讲到的原理保存到缓存,取缓存;但别忘了,一旦你对该表有任何的修改增加或删除操作,记得要在对应的位置放入刷新缓存的方法:oscache.flushEntry("servers");

这样,当下一次调用上述自己封装的,从缓存取结果的方法时,重新进入【取不到则从库里取,如果从库里取到了结果,则把结果缓存起来,再把结果返回】逻辑分支。因为当你执行了oscache.flushEntry("servers");这行代码时,缓存得到了更新,该servers key就不再对应之前的结果了。自然而然的你此时尝试从缓存取不到结果了,所以又得重新查库,把最新的查询结果缓存起来并返回给你。

 

重点来了!上面说到的key比较好理解,就是一常量。但当你需要参数去查询缓存的时候呢?依然拿机器的例子来说:假设你现在需要根据机器的id以及机器的使用时间判断它是否还能使用 (假设机器一个id可对应多个使用时间,机器id是不能随意删除的,因为它对应多个响应时间)(即是否还能使用由机器id和机器使用时间共同决定)。那你的key该如何设计呢?

(其实理解到这儿,我们不妨把第一种无需传过滤条件的缓存作为一种特殊的”父条件“。从这个角度来看,父条件一般是常量或是不能直接被修改的变量 微笑

如果你把key设置为id + 使用时间。从缓存里取结果是可以的,相当于把id + 时间作为缓存的key,对应的使用状态作为value保存起来下次直接取就好了。但如果我修改了机器的使用时间呢?flushEntry(id + time)会生效吗?——答案是不会(本人亲测)。究其原因,time已经是一个新的值了,从而id + time这个key也跟之前的id+time这个key不同了。你不可能指望缓存直接flush一个新的Key吧!同样,增加一个机器的使用时间,time都是新的,更加不用说了;删除呢?删掉一个机器的使用时间,这个time这个都不存在啦,你更不可能去刷新一个不存在的key吧。

 

所以,在这种根据一个父条件(这里指机器id)的基础上,再根据一个子条件(这里指使用时间)去共同查询结果(即sql里会有这2个或多个需要过滤的条件,这里就考虑俩好了)的情形。我们必须只把父条件作为缓存的key,而子条件不能作为放入到key里面。因为子条件是可能被修改删除和增加的。刷新缓存的时候,它会重新从sql里取结果。只刷新父条件,再次访问从缓存取的方法时,会进入Pojo的查询数据库的方法。

重点是我们如何在Pojo查询数据库并返回结果的方法里返回怎样的数据结构。比如举的这个例子里,你不妨把使用时间作为key,使用状态作为value,返回一个map。到时候你根据查询缓存的方法去取时,缓存的key依然是机器id,只不过取到的不是该机器id对应的所有使用状态信息罢了,结果是你再根据另一个取的条件time,用map.get(time)就好啦!——即你一旦flush了机器id这个缓存key之后,会得到的是一个”新的“以第2个过滤条件time作为key,对应使用状态作为value的map了。你再通过该map get一个新的time,值自然也出来了。当然,从db查询后返回结果情况的不同,数据结构的选择也不同,这是很重要的,决定了我们从缓存拿数据的效率。

 

总 而 言 之:

1)做缓存一般是在高并发的情形下,一般的增删改查做缓存没有意义;

2)不要只记得保存结果到缓存和从缓存取结果,还别忘记当有数据修改时,要刷新缓存,否则你拿到的缓存中的数据将会和数据库中最新的结果不同步;

3)查缓存和取缓存的key必须一致;当查询缓存的有过滤条件时,缓存所选择的key不能是”子条件“,而是不能直接做修改操作(包括增。删、改)的父条件。

 

======================  oscache刷新缓存理解end =======================

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [oscache 缓存 flushentry] 推荐:

oscache之刷新缓存flushEntry的使用

- - ITeye博客
==========================  困扰和痛苦多时的oscache刷新缓存start  =======================. Everyday都不同于2015-9-19 周六15:30 . 【前言】一般而言,oscache缓存常用于在高并发的情形下. 当你初次调用缓存的方法时,如果缓存中还没有响应的key,则会去执行底层的sql语句,并把结果缓存起来.

OSCache缓存监控实现

- - ITeye博客
最近一个项目用到OsCache的页面片段缓存,google了一下居然没有找到OsCache的监控工具(list all keys from cache),于是大略读了一下OSCache-2.4.1的源码,发现Cache.java类的cacheMap定义成了私有变量,如下:. 所以只能用反射机制暴力破解了(按照Sun公司的JVM规范是许可的^_^),主要代码如下:.

OSCache 缓存重建在 Race Condition 下的 NRE 问题

- - 博客园_旁观者
高并发情况下,使用 OSCache 作为本地缓存中间件的前端服务,日志文件中会出现大量如下错误信息:. 无论你使用哪一种本地缓存中间件,如果你缓存数据片段时设置了过期时间,都需要考虑缓存失效后的缓存重建(repopulate the cache)场景. 进一步必须考虑 Race Condition (同进程下多线程,或不同进程)下如何重建.

缓存算法

- lostsnow - 小彰
没有人能说清哪种缓存算法由于其他的缓存算法. (以下的几种缓存算法,有的我也理解不好,如果感兴趣,你可以Google一下  ). 大家好,我是 LFU,我会计算为每个缓存对象计算他们被使用的频率. 我是LRU缓存算法,我把最近最少使用的缓存对象给踢走. 我总是需要去了解在什么时候,用了哪个缓存对象.

Hibernate 缓存

- - ITeye博客
1数据缓存:(date caching) 是一种将数据暂时存于内存缓存去中的技术,缓存通常是影响系统性能的关键因素. 2.ORM的数据缓存策略有3中.   1.事务级缓存:  分为 数据库事务和 应用级事务,是基于Session的生命周期的实现,每个session都会在内部维持一个数据缓存, 随session的创建和消亡.

hibernate缓存,一级缓存,二级缓存,查询缓存

- - CSDN博客推荐文章
1、缓存是数据库数据在内存中的临时容器,它包含了库表数据在内存中的临时拷贝,位于数据库和访问层之间. 2、ORM在进行数据读取时,会根据缓存管理策略,首先在缓冲中查询,如果发现,则直接使用,避免数据库调用的开销. 事务级缓存:当前事务范围内的数据缓存. 应用级缓存:某个应用中的数据缓存. 分布式缓存:多个应用,多个JVM之间共享缓存.

缓存相关——缓存穿透、缓存并发、缓存失效、缓存预热、缓存雪崩、缓存算法

- - 编程语言 - ITeye博客
我们在项目中使用缓存通常都是先检查缓存中是否存在,如果存在直接返回缓存内容,如果不存在就直接查询数据库然后再缓存查询结果返回. 这个时候如果我们查询的某一个数据在缓存中一直不存在,就会造成每一次请求都查询DB,这样缓存就失去了意义,在流量大时,可能DB就挂掉了. 要是有人利用不存在的key频繁攻击我们的应用,这就是漏洞.

Hibernate 二级缓存

- - CSDN博客推荐文章
很多人对二级缓存都不太了解,或者是有错误的认识,我一直想写一篇文章介绍一下hibernate的二级缓存的,今天终于忍不住了. 我的经验主要来自hibernate2.1版本,基本原理和3.0、3.1是一样的,请原谅我的顽固不化. hibernate的session提供了一级缓存,每个session,对同一个id进行两次load,不会发送两条sql给数据库,但是session关闭的时候,一级缓存就失效了.

App缓存管理

- - ITeye博客
无论大型或小型应用,灵活的缓存可以说不仅大大减轻了服务器的压力,而且因为更快速的用户体验而方便了用户. Android的apk可以说是作为小型应用,其中99%的应用并不是需要实时更新的,而且诟病于蜗牛般的移动网速,与服务器的数据交互是能少则少,这样用户体验才更好,这也是我们有时舍弃webview而采用json传输数据的原因之一.