遇到问题7 C% Y3 b/ s; }6 |2 G- X V3 i' r
4 V2 H0 j( N4 z9 G5 u! |$ B! q
项目建设过程中遇到一个问题:使用频率很高的基础数据(8MB),缓存到redis 存取效率不高。存在多条这样的基础数据。
! `5 X; \* Z, Q8 ^9 a) L1 \ J8 U* u2 i
9 q) T5 B% K; O# r3 i
当然,可以用ListenableFutureCallback结合CountDownLatch做并发和结果聚合。(前提是获取缓存数据的操作不存在先后关系)
/ J2 M1 v2 O0 D但每次接口调用仍会有零点几秒消耗在查询Redis上。
! \1 Q5 C4 j/ f1 _0 l' ]2 s自行实现二级缓存又存在一致性问题:, G7 A* y. c" D( j' |0 w
即使有定时刷新作业,也会出现:一段时间内同一个应用的不同实例本地缓存的数据不一致的情况。
1 Q: J0 }+ x# A2 k因为更新的时候无法更新所有应用实例的本地缓存。9 l" C1 k9 ?( p u# [2 H b, |4 b% r
举栗说明:
8 @+ Y* i+ U. |- z/ G应用A借助JetCache实现二级缓存。部署时创建两个实例4 M( i: F( W" Z
实例1修改了一条数据,并更新到数据库、local cache、remote cache中。
& X* y6 {, N B4 \5 c实例2在本地缓存刷新或过期前,local cache中的数据仍是旧的。
* n4 Q6 z" o* A1 W6 c9 |5 @; V数据不一致的最长时间取决于缓存刷新作业的执行周期
* Y2 `! {1 Y/ B5 u! |& v后面goolge到了阿里开源的二级缓存组件JetCache,做了对比实验:# \* X) h8 s" Z6 a
Redisson(redis)VS JetCache 一级缓存(redis) VS JetCache 二级缓存(redis + caffeine)
" `& f* O% B; C8 D9 q) C7 ]/ f+ p/ P* `
配置文件:
' t6 K0 ]6 u. U' ? \, W. V6 {) C) r7 r1 v" k: q; {
9 l) [) i" A" N5 n
单元测试方法:/ { D% U1 F3 f5 P @Test public void compareEfficiency() throws IOException { //保障数据都加载到缓存中 airportCacheDao.getAllFromRedisson(); long start = System.currentTimeMillis(); for (int i = 0; i < 20; i++) { airportCacheDao.getAllFromRedisson(); } long end = System.currentTimeMillis(); System.out.println(String.format("通过Redisson查询redis;20次全量数据,耗时:%s ms", end - start)); //保障数据都加载到缓存中 airportCacheDao.getAllFromDbOrJetCacheRemote(); long start1 = System.currentTimeMillis(); for (int i = 0; i < 20; i++) { airportCacheDao.getAllFromDbOrJetCacheRemote(); } long end1 = System.currentTimeMillis(); System.out.println(String.format("通过JetCache查询redis;20次全量数据,耗时:%s ms", end1 - start1)); //保障数据都加载到缓存中 airportCacheDao.getAllFromDbOrJetCacheBoth(); long start2 = System.currentTimeMillis(); for (int i = 0; i < 20; i++) { airportCacheDao.getAllFromDbOrJetCacheBoth(); } long end2 = System.currentTimeMillis(); System.out.println(String.format("通过JetCache二级缓存查询;20次全量数据,耗时:%s ms", end2 - start2)); System.in.read(); }
; O0 F% e( W; ]3 P0 Z0 P4 a~5 C6 `- c @! q
DAO中的方法
5 [- [" P; c1 s0 n; U8 i public List getAllFromRedisson() { List airports; RBucket airportRList = redissonClient.getBucket(RedisKey.MDMAirportDataList+"_Redisson"); if (airportRList.isExists()) { //System.out.println("get from cache"); airports = airportRList.get(); } else { System.out.println("get from db"); airports = iMdBmdmAirportDao.list(null); airportRList.set(airports); } return airports; } @Cached(name = RedisKey.MDMAirportDataList+"_JetCache_Remote", cacheType = CacheType.REMOTE, expire = 1000) public List getAllFromDbOrJetCacheRemote() { System.out.println("get from db"); List airports; airports = iMdBmdmAirportDao.list(null); return airports; } @Cached(name = RedisKey.MDMAirportDataList+"_JetCache_Both", cacheType = CacheType.BOTH, expire = 1000) public List getAllFromDbOrJetCacheBoth() { System.out.println("get from db"); List airports; airports = iMdBmdmAirportDao.list(null); return airports; }
从对比实验中可看出查询效率差别巨大。
7 E Y) Z: m0 X, \0 |* h说明:: {: F1 o; n