本文共 8238 字,大约阅读时间需要 27 分钟。
spring boot中集成了spring cache,并有多种缓存方式的实现,如:Redis、Caffeine、JCache、EhCache等等。但如果只用一种缓存,要么会有较大的网络消耗(如Redis),要么就是内存占用太大(如Caffeine这种应用内存缓存)。在很多场景下,可以结合起来实现一、二级缓存的方式,能够很大程度提高应用的处理效率。
缓存、两级缓存
spring cache:主要包含spring cache定义的接口方法说明和注解中的属性说明
spring boot + spring cache:RedisCache实现中的缺陷
caffeine简介
spring boot + spring cache 实现两级缓存(redis + caffeine)
简单的理解,缓存就是将数据从读取较慢的介质上读取出来放到读取较快的介质上,如磁盘-->内存。平时我们会将数据存储到磁盘上,如:数据库。如果每次都从数据库里去读取,会因为磁盘本身的IO影响读取速度,所以就有了像redis这种的内存缓存。可以将数据读取出来放到内存里,这样当需要获取数据时,就能够直接从内存中拿到数据返回,能够很大程度的提高速度。但是一般redis是单独部署成集群,所以会有网络IO上的消耗,虽然与redis集群的链接已经有连接池这种工具,但是数据传输上也还是会有一定消耗。所以就有了应用内缓存,如:caffeine。当应用内缓存有符合条件的数据时,就可以直接使用,而不用通过网络到redis中去获取,这样就形成了两级缓存。应用内缓存叫做一级缓存,远程缓存(如redis)叫做二级缓存
4.0.0 com.learn springboot2.0-redis 0.0.1-SNAPSHOT org.springframework.boot spring-boot-starter-parent 2.0.0.RELEASE com.alibaba fastjson 1.2.47 org.apache.commons commons-lang3 org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-data-redis org.springframework.boot spring-boot-starter-cache net.sf.ehcache ehcache 2.9.1 org.mybatis.spring.boot mybatis-spring-boot-starter 1.1.1 mysql mysql-connector-java
spring: datasource: url: jdbc:mysql://localhost:3306/test username: root password: 123456 driver-class-name: com.mysql.jdbc.Driver test-while-idle: true test-on-borrow: true validation-query: SELECT 1 FROM DUAL time-between-eviction-runs-millis: 300000 min-evictable-idle-time-millis: 1800000 redis: database: 0 host: localhost port: 6379 password: 123456 jedis: pool: max-active: 8 max-wait: -1 max-idle: 8 min-idle: 0 timeout: 10000 # 缓存配置读取 cache: type: ehcache ehcache: config: classpath:app1_ehcache.xml
package com.learn.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.cache.ehcache.EhCacheCacheManager;import org.springframework.stereotype.Component;import net.sf.ehcache.Cache;import net.sf.ehcache.Element;@Componentpublic class EhCacheUtils { // @Autowired // private CacheManager cacheManager; @Autowired private EhCacheCacheManager ehCacheCacheManager; // 添加本地缓存 (相同的key 会直接覆盖) public void put(String cacheName, String key, Object value) { Cache cache = ehCacheCacheManager.getCacheManager().getCache(cacheName); Element element = new Element(key, value); cache.put(element); } // 获取本地缓存 public Object get(String cacheName, String key) { Cache cache = ehCacheCacheManager.getCacheManager().getCache(cacheName); Element element = cache.get(key); return element == null ? null : element.getObjectValue(); } public void remove(String cacheName, String key) { Cache cache = ehCacheCacheManager.getCacheManager().getCache(cacheName); cache.remove(key); }}
package com.learn.service;import java.util.Set;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.core.StringRedisTemplate;import org.springframework.stereotype.Component;@Componentpublic class RedisService { @Autowired private StringRedisTemplate stringRedisTemplate; // public void set(String key, Object object, Long time) { // stringRedisTemplate.opsForValue(); // // 存放String 类型 // if (object instanceof String) { // setString(key, object); // } // // 存放 set类型 // if (object instanceof Set) { // setSet(key, object); // } // // 设置有效期 以秒为单位 // stringRedisTemplate.expire(key, time, TimeUnit.SECONDS); // } // public void setString(String key, Object object) { // 开启事务权限 stringRedisTemplate.setEnableTransactionSupport(true); try { // 开启事务 begin stringRedisTemplate.multi(); String value = (String) object; stringRedisTemplate.opsForValue().set(key, value); System.out.println("存入完毕,马上开始提交redis事务"); // 提交事务 stringRedisTemplate.exec(); } catch (Exception e) { // 需要回滚事务 stringRedisTemplate.discard(); } } public void setSet(String key, Object object) { Setvalue = (Set ) object; for (String oj : value) { stringRedisTemplate.opsForSet().add(key, oj); } } public String getString(String key) { return stringRedisTemplate.opsForValue().get(key); }}
package com.learn.service;import org.apache.commons.lang3.StringUtils;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;import com.alibaba.fastjson.JSONObject;import com.learn.entity.Users;import com.learn.mapper.UserMapper;@Servicepublic class UserService { @Autowired private EhCacheUtils ehCacheUtils; private static final String CACHENAME_USERCACHE = "userCache"; @Autowired private RedisService redisService; @Autowired private UserMapper userMapper; public Users getUser(Long id) { String key = this.getClass().getName() + "-" + Thread.currentThread().getStackTrace()[1].getMethodName() + "-id:" + id; // 1.先查找一级缓存(本地缓存),如果本地缓存有数据直接返回 Users ehUser = (Users) ehCacheUtils.get(CACHENAME_USERCACHE, key); if (ehUser != null) { System.out.println("使用key:" + key + ",查询一级缓存 ehCache 获取到ehUser:" + JSONObject.toJSONString(ehUser)); return ehUser; } // 2. 如果本地缓存没有该数据,直接查询二级缓存(redis) String redisUserJson = redisService.getString(key); if (!StringUtils.isEmpty(redisUserJson)) { // 将json 转换为对象(如果二级缓存redis中有数据直接返回二级缓存) JSONObject jsonObject = new JSONObject(); Users user = jsonObject.parseObject(redisUserJson, Users.class); // 更新一级缓存 ehCacheUtils.put(CACHENAME_USERCACHE, key, user); System.out.println("使用key:" + key + ",查询二级缓存 redis 获取到ehUser:" + JSONObject.toJSONString(user)); return user; } // 3. 如果二级缓存redis中也没有数据,查询数据库 Users user = userMapper.getUser(id); if (user == null) { return null; } // 更新一级缓存和二级缓存 String userJson = JSONObject.toJSONString(user); redisService.setString(key, userJson); ehCacheUtils.put(CACHENAME_USERCACHE, key, user); System.out.println("使用key:" + key + ",一级缓存和二级都没有数据,直接查询db" + userJson); return user; }}
package com.learn.api.controller;import java.util.HashSet;import java.util.Set;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import com.learn.entity.Users;import com.learn.service.RedisService;import com.learn.service.UserService;@RestControllerpublic class IndexControler { @Autowired private RedisService redisService; @Autowired private UserService userService; @RequestMapping("/setString") public String setString(String key, String value) { // redisService.set(key, value, 60l); redisService.setString(key, value); return "success"; } @RequestMapping("/getString") public String getString(String key) { return redisService.getString(key); } @RequestMapping("/setSet") public String setSet() { Setset = new HashSet (); set.add("yushengjun"); set.add("lisi"); redisService.setSet("setTest", set); return "success"; } @RequestMapping("/getUser") public Users getUser(Long id) { Users user = userService.getUser(id); return user; }}
package com;import org.mybatis.spring.annotation.MapperScan;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cache.annotation.EnableCaching;@SpringBootApplication@MapperScan("com.learn.mapper")@EnableCachingpublic class AppRedis { public static void main(String[] args) { SpringApplication.run(AppRedis.class, args); }}
转载地址:http://ubkzb.baihongyu.com/