Spring Boot2.0+Redis+Ehcache实现二级缓存详解编程语言

EHCache 本地缓存

Redis 分布式缓存(可以共享)

 

一级 Redis 二级Ehcache    当redis挂了 有备胎

 反之:

  先走本地,本地没有再走网络  尽量少走Redis  效率会高一些

 

Redis与数据库的区别: 

   相同点 都是需要进行网络连接

  不同点 是存放的介质  内存 和 硬盘  

   数据库需要做IO操作 性能比直接操作内存效率要低

 

 Ehchache

   不需要走网络 直接从内存中获取

    由于Ehchache容器限制,会持久化在硬盘上,

   

 

 

 

Redis+ehCache实现两级级缓存

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)叫做二级缓存

Spring Boot2.0+Redis+Ehcache实现二级缓存详解编程语言

Spring Boot2.0+Redis+Ehcache实现二级缓存详解编程语言

过期问题:

一级缓存 过期 时间 比二级要短一些

 

目录结构:

 Spring Boot2.0+Redis+Ehcache实现二级缓存详解编程语言

实体类:

注意一定要 序列号

package com.toov5.entity; 
 
import java.io.Serializable; 
 
import lombok.Data; 
 
@Data 
public class Users implements Serializable{ 
  private String name; 
  private Integer age; 
}

controller层

package com.toov5.controller; 
 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.web.bind.annotation.RequestMapping; 
import org.springframework.web.bind.annotation.RestController; 
 
import com.toov5.entity.Users; 
import com.toov5.service.UserService; 
 
@RestController 
public class IndexController { 
    @Autowired 
    private UserService userService; 
     
    @RequestMapping("/userId") 
    public Users getUserId(Long id){ 
        return userService.getUser(id);   
    } 
     
 
}

service层

package com.toov5.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; 
 
 
@Component 
public 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.toov5.service; 
 
import java.util.Set; 
import java.util.concurrent.TimeUnit; 
 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.data.redis.core.StringRedisTemplate; 
import org.springframework.stereotype.Component; 
 
@Component 
public class RedisService { 
 
    @Autowired 
    private StringRedisTemplate stringRedisTemplate; 
    //这样该方法支持多种数据类型  
    public void set(String key , Object object, Long time){ 
        //开启事务权限 
        stringRedisTemplate.setEnableTransactionSupport(true); 
        try { 
            //开启事务 
            stringRedisTemplate.multi(); 
             
            String argString =(String)object;  //强转下 
            stringRedisTemplate.opsForValue().set(key, argString); 
             
            //成功就提交 
            stringRedisTemplate.exec(); 
        } catch (Exception e) { 
            //失败了就回滚 
            stringRedisTemplate.discard(); 
             
        } 
        if (object instanceof String ) {  //判断下是String类型不 
            String argString =(String)object;  //强转下 
            //存放String类型的 
            stringRedisTemplate.opsForValue().set(key, argString); 
        } 
        //如果存放Set类型 
        if (object instanceof Set) { 
            Set<String> valueSet =(Set<String>)object; 
            for(String string:valueSet){ 
                stringRedisTemplate.opsForSet().add(key, string);  //此处点击下源码看下 第二个参数可以放好多 
            } 
        } 
        //设置有效期 
        if (time != null) { 
            stringRedisTemplate.expire(key, time, TimeUnit.SECONDS); 
        } 
         
    } 
    //做个封装 
    public void setString(String key, Object object){ 
        String argString =(String)object;  //强转下 
        //存放String类型的 
        stringRedisTemplate.opsForValue().set(key, argString); 
    } 
    public void setSet(String key, Object object){ 
        Set<String> valueSet =(Set<String>)object; 
        for(String string:valueSet){ 
            stringRedisTemplate.opsForSet().add(key, string);  //此处点击下源码看下 第二个参数可以放好多 
        } 
    } 
     
    public String getString(String key){ 
     return    stringRedisTemplate.opsForValue().get(key); 
    } 
     
     
}
package com.toov5.service; 
 
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.stereotype.Component; 
 
import com.alibaba.fastjson.JSONObject; 
import com.toov5.entity.Users; 
import com.toov5.mapper.UserMapper; 
 
import ch.qos.logback.core.net.SyslogOutputStream; 
import io.netty.util.internal.StringUtil; 
 
@Component 
public class UserService { 
    @Autowired 
    private EhCacheUtils ehCacheUtils; 
    @Autowired 
    private RedisService redisService; 
    @Autowired 
    private UserMapper userMapper; 
    //定义个全局的cache名字 
    private String cachename ="userCache"; 
     
    public Users getUser(Long id){ 
        //先查询一级缓存  key以当前的类名+方法名+id+参数值 
        String key = this.getClass().getName() + "-" + Thread.currentThread().getStackTrace()[1].getMethodName() 
                + "-id:" + id; 
        //查询一级缓存数据有对应值的存在 如果有 返回 
        Users user = (Users)ehCacheUtils.get(cachename, key); 
        if (user != null) { 
            System.out.println("key"+key+",直接从一级缓存获取数据"+user.toString()); 
            return user; 
        } 
        //一级缓存没有对应的值存在,接着查询二级缓存     
        // redis存对象的方式  json格式 然后反序列号 
        String userJson = redisService.getString(key); 
        //如果rdis缓存中有这个对应的值,修改一级缓存    最下面的会有的 相同会覆盖的     
        if (!StringUtil.isNullOrEmpty(userJson)) {  //有 转成json 
            JSONObject jsonObject = new JSONObject();//用的fastjson 
            Users resultUser = jsonObject.parseObject(userJson,Users.class); 
            ehCacheUtils.put(cachename, key, resultUser); 
            return resultUser; 
        } 
        //都没有 查询DB  
        Users user1 = userMapper.getUser(id); 
        if (user1 == null) { 
            return null; 
        } 
        //存放到二级缓存 redis中 
        redisService.setString(key, new JSONObject().toJSONString(user1)); 
        //存放到一级缓存 Ehchache 
        ehCacheUtils.put(cachename, key, user1); 
        return user1; 
    } 
     
     
     
}

启动类:

package com.toov5.app; 
 
import org.mybatis.spring.annotation.MapperScan; 
import org.springframework.boot.SpringApplication; 
import org.springframework.boot.autoconfigure.SpringBootApplication; 
import org.springframework.cache.annotation.EnableCaching; 
 
@EnableCaching //开启缓存 
@MapperScan(basePackages={"com.toov5.mapper"}) 
@SpringBootApplication(scanBasePackages={"com.toov5.*"}) 
public class app { 
   public static void main(String[] args) { 
    SpringApplication.run(app.class, args); 
} 
     
}

app1.ehcache.xml

<?xml version="1.0" encoding="UTF-8"?> 
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"> 
 
	<diskStore path="java.io.tmpdir/ehcache-rmi-4000" /> 
 
 
	<!-- 默认缓存 --> 
	<defaultCache maxElementsInMemory="1000" eternal="true" 
		timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" 
		diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000" 
		diskPersistent="true" diskExpiryThreadIntervalSeconds="120" 
		memoryStoreEvictionPolicy="LRU"> 
	</defaultCache> 
   
	<!-- demo缓存 --><!-- name="userCache" 对应我们在 @CacheConfig(cacheNames={"userCache"}) !!!!! --> 
	<!--Ehcache底层也是用Map集合实现的 --> 
	<cache name="userCache" maxElementsInMemory="1000" eternal="false" 
		timeToIdleSeconds="120" timeToLiveSeconds="120" overflowToDisk="true" 
		diskSpoolBufferSizeMB="30" maxElementsOnDisk="10000000" 
		diskPersistent="false" diskExpiryThreadIntervalSeconds="120" 
		memoryStoreEvictionPolicy="LRU">  <!-- LRU缓存策略 --> 
		<cacheEventListenerFactory 
			class="net.sf.ehcache.distribution.RMICacheReplicatorFactory" /> 
		<!-- 用于在初始化缓存,以及自动设置 --> 
		<bootstrapCacheLoaderFactory 
			class="net.sf.ehcache.distribution.RMIBootstrapCacheLoaderFactory" /> 
	</cache> 
</ehcache> 

yml

###端口号配置 
server: 
  port: 8080 
###数据库配置   
spring: 
  datasource: 
    url: jdbc:mysql://localhost:3306/test 
    username: root 
    password: root 
    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 
# 缓存配置读取 
  cache: 
    type: ehcache 
    ehcache: 
      config: classpath:app1_ehcache.xml 
 
  redis: 
    database: 0    
    host:  192.168.91.3 
    port:  6379 
    password:  123 
    jedis: 
      pool: 
        max-active: 8 
        max-wait: -1 
        max-idle: 8 
        min-idle: 0 
    timeout: 10000 

运行结果:

Spring Boot2.0+Redis+Ehcache实现二级缓存详解编程语言

 

 Spring Boot2.0+Redis+Ehcache实现二级缓存详解编程语言

 

 后面的访问,在控制台打印:

Spring Boot2.0+Redis+Ehcache实现二级缓存详解编程语言

 

 

其中一定要注意

 Spring Boot2.0+Redis+Ehcache实现二级缓存详解编程语言

这里的 两级缓存时间问题  执行设置二级缓存时候 需要时间的  所以这两个时间设置问题一定要注意了哦

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/16145.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论