- 之前有写过springboot+redis实战,这里不做redis缓存测试了
-
下方有springboot+ehcache的测试用例
目录:
应用场景目录.png
应用场景:
1.ehcache
ehcache直接在jvm虚拟机中缓存,速度快,效率高;但是缓存共享麻烦,集群分布式应用不方便。
2.redis
redis是通过socket访问到缓存服务,效率比ecache低,比数据库要快很多,处理集群和分布式缓存方便,有成熟的方案。
- 如果是单个应用或者对缓存访问要求很高的应用,用ehcache。
- 如果是大型系统,存在缓存共享、分布式部署、缓存内容很大的,建议用redis。
补充下:ehcache也有缓存共享方案,不过是通过RMI或者Jgroup多播方式进行广播缓存通知更新,缓存共享复杂,维护不方便;简单的共享可以,但是涉及到缓存恢复,大数据缓存,则不合适
3.memcached
是一种高性能、分布式对象缓存系统,最初设计于缓解动态网站数据库加载数据的延迟性,你可以把它想象成一个大的内存HashTable,就是一个key-value键值缓存。保存的对象并不是持久的,服务停止之后,里边的数据就会丢失。
4.总结
-
redis和memcached相比的独特之处:
1、redis可以用来做存储(storage),而memcached是用来做缓存(cache),redis的速度比memcached快很多。
这个特点主要因为其有持久化功能
2、数据支持类型:redis中存储的数据有多种结构,而memcached存储的数据只有一种类型“字符串”
3、存储方式:Memecache把数据全部存在内存之中,断电后会挂掉,数据不能超过内存大小。Redis有部份存在硬盘上,这样能保证数据的持久性
4、使用底层模型不同:它们之间底层实现方式 以及与客户端之间通信的应用协议不一样。Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求
5、value大小:redis最大可以达到1GB,而memcache只有1MB -
redis和ehcache相比:
1、Redis:属于独立的运行程序,需要单独安装后,使用JAVA中的Jedis来操纵。因为它是独立,所以如果你写个单元测试程序,放一些数据在Redis中,然后又写一个程序去拿数据,那么是可以拿到这个数据的。
2、ehcache:与Redis明显不同,它与java程序是绑在一起的,java程序活着,它就活着。譬如,写一个独立程序放数据,再写一个独立程序拿数据,那么是拿不到数据的。只能在独立程序中才能拿到数据
5.仅仅演示springboot+ehcache代码:
1、ehcache依赖:
<!-- Spring Boot 缓存支持启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Ehcache 坐标 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
具体的pom文件
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--事务 @Transactional-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
<!--集成swagger-API-->
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
<!--<scope>compile</scope>-->
</dependency>
<dependency>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
<!--<scope>compile</scope>-->
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<exclusions>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-models</artifactId>
</exclusion>
<exclusion>
<groupId>io.swagger</groupId>
<artifactId>swagger-annotations</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Spring Boot 缓存支持启动器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Ehcache 坐标 -->
<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
</dependencies>
2、创建ehcache.xml文件
在resources下面创建ehcache.xml,复制以下内容。
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../config/ehcache.xsd">
<!--这个是磁盘存储路径,当内存缓存满了的时候,就会往这里面放,java.io.tmpdir是操作系统缓存的临时目录,不同操作系统缓存目录不一样。-->
<diskStore path="java.io.tmpdir"/>
<!--defaultCache:echcache的默认缓存策略 -->
<defaultCache
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</defaultCache>
<!--测试:自定义缓存策略1-->
<cache name="users"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</cache>
<!--测试:自定义缓存策略2-->
<cache name="users2"
maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
maxElementsOnDisk="10000000"
diskExpiryThreadIntervalSeconds="120"
memoryStoreEvictionPolicy="LRU">
<persistence strategy="localTempSwap"/>
</cache>
</ehcache>
<!--
maxElementsInMemory 内存缓存中最多可以存放的元素数量,若放入Cache中的元素超过这个数值,则有以下两种情况
1)若overflowToDisk=true,则会将Cache中多出的元素放入磁盘文件中
2)若overflowToDisk=false,则根据memoryStoreEvictionPolicy策略替换Cache中原有的元素
overflowToDisk 内存不足时,是否启用磁盘缓存
eternal 缓存中对象是否永久有效
timeToIdleSeconds 缓存数据在失效前的允许闲置时间(单位:秒),仅当eternal=false时使用,默认值是0表示可闲置时间无穷大,若超过这个时间没有访问此Cache中的某个元素,那么此元素将被从Cache中清除
timeToLiveSeconds 缓存数据的总的存活时间(单位:秒),仅当eternal=false时使用,从创建开始计时,失效结束。
maxElementsOnDisk 磁盘缓存中最多可以存放的元素数量,0表示无穷大
diskExpiryThreadIntervalSeconds 磁盘缓存的清理线程运行间隔,默认是120秒
memoryStoreEvictionPolicy 内存存储与释放策略,即达到maxElementsInMemory限制时,Ehcache会根据指定策略清理内存 共有三种策略,分别为LRU(最近最少使用)、LFU(最常用的)、FIFO(先进先出)
另外,defaultCache是默认缓存方式,cache是自定义的缓存方式,自行设置name
-->
<!--timeToIdleSeconds 当缓存闲置n秒后销毁 -->
<!--timeToLiveSeconds 当缓存存活n秒后销毁 -->
<!-- 缓存配置
name:缓存名称。
maxElementsInMemory:缓存最大个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
overflowToDisk:当内存中对象数量达到maxElementsInMemory时,Ehcache将会对象写到磁盘中。 diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
maxElementsOnDisk:硬盘最大缓存个数。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk
store persists between restarts of the Virtual Machine. The default value
is false.
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。 memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是
LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。 -->
3、参考ehcache.xml配置文件详解
diskStore:为缓存路径,ehcache分为内存和磁盘两级,此属性定义磁盘的缓存位置。
defaultCache:默认缓存策略,当ehcache找不到定义的缓存时,则使用这个缓存策略。只能定义一个。
name:缓存名称。
maxElementsInMemory:缓存最大数目
maxElementsOnDisk:硬盘最大缓存个数。
eternal:对象是否永久有效,一但设置了,timeout将不起作用。
overflowToDisk:是否保存到磁盘,当系统宕机时
timeToIdleSeconds:设置对象在失效前的允许闲置时间(单位:秒)。仅当eternal=false对象不是永久有效时使用,可选属性,默认值是0,也就是可闲置时间无穷大。
timeToLiveSeconds:设置对象在失效前允许存活时间(单位:秒)。最大时间介于创建时间和失效时间之间。仅当eternal=false对象不是永久有效时使用,默认是0.,也就是对象存活时间无穷大。
diskPersistent:是否缓存虚拟机重启期数据 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:这个参数设置DiskStore(磁盘缓存)的缓存区大小。默认是30MB。每个Cache都应该有自己的一个缓冲区。
diskExpiryThreadIntervalSeconds:磁盘失效线程运行时间间隔,默认是120秒。
memoryStoreEvictionPolicy:当达到maxElementsInMemory限制时,Ehcache将会根据指定的策略去清理内存。默认策略是LRU(最近最少使用)。你可以设置为FIFO(先进先出)或是LFU(较少使用)。
clearOnFlush:内存数量最大时是否清除。
FIFO,first in first out,先进先出。
LFU, Less Frequently Used,一直以来最少被使用的。如上面所讲,缓存的元素有一个hit属性,hit值最小的将会被清出缓存。
LRU,Least Recently Used,最近最少使用的,缓存的元素有一个时间戳,当缓存容量满了,而又需要腾出地方来缓存新的元素的时候,那么现有缓存元素中时间戳离当前时间最远的元素将被清出缓存。
4、添加启用缓存
@EnableCaching //添加启用缓存
***Application.java
@SpringBootApplication
@EnableCaching //添加启用缓存
public class SpringbootTestCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootTestCacheApplication.class, args);
}
}
5、EhcacheController.java
package com.dist.controller;
import com.dist.entity.UserEntity;
import com.dist.service.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;
/**Ehcache 缓存
* @author zhengja@dist.com.cn
* @data 2019/8/15 10:51
*/
@Api(tags = {"EhcacheController"},description = "Ehcache 缓存")
@RestController
@RequestMapping(value = "rest/ehcache")
public class EhcacheController {
@Autowired
@Qualifier(value = "userServiceImpl")
private UserService userService;
@ApiOperation(value = "保存缓存-users",httpMethod = "GET")
@RequestMapping(value = "getuserbyid",method = RequestMethod.GET)
public Object getUserById(@ApiParam(value = "查询字段:id") @RequestParam Integer id){
//缓存存放的目录
String userHome = System.getProperty("user.home");
String userDir = System.getProperty("user.dir");
String tmpDir = System.getProperty("java.io.tmpdir");
System.out.println("userHome:"+userHome);
System.out.println("userDir:"+userDir);
System.out.println("tmpDir:"+tmpDir);
return this.userService.getUserById(id);
}
@ApiOperation(value = "清除缓存-users",httpMethod = "POST")
@RequestMapping(value = "saveuser",method = RequestMethod.POST)
public void saveUser(@ApiParam(value = "参考:Model") @RequestBody UserEntity userEntity){
this.userService.saveUser(userEntity);
}
@ApiOperation(value = "模拟数据库保存",httpMethod = "GET")
@RequestMapping(value = "save",method = RequestMethod.GET)
public String save(@ApiParam(value = "typeId") @RequestParam String typeId) {
return this.userService.save(typeId);
}
@ApiOperation(value = "模拟数据库更新",httpMethod = "GET")
@RequestMapping(value = "update",method = RequestMethod.GET)
public String update(@ApiParam(value = "typeId") @RequestParam String typeId) {
return this.userService.update(typeId);
}
@ApiOperation(value = "模拟数据库删除",httpMethod = "GET")
@RequestMapping(value = "delete",method = RequestMethod.GET)
public String delete(@ApiParam(value = "typeId") @RequestParam String typeId) {
return this.userService.delete(typeId);
}
@ApiOperation(value = "模拟数据库查询",httpMethod = "GET")
@RequestMapping(value = "select",method = RequestMethod.GET)
public String select(@ApiParam(value = "typeId") @RequestParam String typeId) {
return this.userService.select(typeId);
}
@ApiOperation(value = "复杂的缓存规则",httpMethod = "GET")
@RequestMapping(value = "selectbyname",method = RequestMethod.GET)
public UserEntity selectByName(@ApiParam(value = "name") @RequestParam String name) {
return this.userService.selectByName(name);
}
}
6、接口UserService.java
package com.dist.service;
import com.dist.entity.UserEntity;
/**
* @author zhengja@dist.com.cn
* @data 2019/8/15 10:36
*/
public interface UserService {
/**
* 测试 ehcache 保存缓存
* @param id
* @return
*/
UserEntity getUserById(Integer id);
/**
* 测试 ehcache 清楚缓存
* @param userEntity
*/
void saveUser(UserEntity userEntity);
/**
* 以下为模拟数据库操作
* @param typeId
* @return
*/
String save(String typeId);
String update(String typeId);
String delete(String typeId);
String select(String typeId);
/**
* 复杂的缓存
* @param name
* @return
*/
UserEntity selectByName(String name);
}
7、实现类UserServiceImpl.java
package com.dist.service.Impl;
import com.dist.entity.UserEntity;
import com.dist.service.UserService;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
/**
* @author zhengja@dist.com.cn
* @data 2019/8/15 10:36
*/
@Service(value = "userServiceImpl")
@Transactional //事务,表示该类下所有的都受事务控制
@CacheConfig(cacheNames = "users2")
public class UserServiceImpl implements UserService {
/**
* 测试 ehcache 保存缓存
*
* @Cacheable 可以标记在一个方法上,也可以标记在一个类上
* 流程:被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法
* 如何缓存:缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果
* @Cacheable可以指定三个属性,value、key和condition
* value属性指定cache的名称(即选择ehcache.xml中哪种缓存方式存储)
* key(默认策略)属性是用来指定Spring缓存方法的返回结果时对应的key的,该属性支持SpringEL表达式;可使用“#参数名”或者“#p参数index”
* condition 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false,只有为 true 才进行缓存 如:condition = "#userName.length()>2"
*
* @param id
* @return
*/
//@Cacheable(value = "users")
@Cacheable(value = {"users","users2"},key = "#id",condition = "#id=1")
@Override
public UserEntity getUserById(Integer id) {
UserEntity userEntity = new UserEntity();
userEntity.setId(id);
userEntity.setName("张三");
userEntity.setAge(23);
System.out.println("id值:"+id);
return userEntity;
}
/**
* 测试 ehcache 清楚缓存
* @param userEntity
*/
@CacheEvict(value = "users",allEntries = true)
@Override
public void saveUser(UserEntity userEntity){
System.out.println(userEntity);
}
/**
* 以下是key 实例
* @param id
* @return
*/
@Cacheable(value="users", key="#id")
public UserEntity find1(Integer id) {
return null;
}
@Cacheable(value="users", key="#p0")
public UserEntity find2(Integer id) {
return null;
}
@Cacheable(value="users", key="#user.id")
public UserEntity find3(UserEntity user) {
return null;
}
@Cacheable(value="users", key="#p0.id")
public UserEntity find4(UserEntity user) {
return null;
}
/**
* 以下是模拟测试
* @param typeId
* @return
*/
@Cacheable
@Override
public String save(String typeId) {
System.out.println("save()执行了=============");
return "模拟数据库保存";
}
@CachePut
@Override
public String update(String typeId) {
System.out.println("update()执行了=============");
return "模拟数据库更新";
}
@CacheEvict
@Override
public String delete(String typeId) {
System.out.println("delete()执行了=============");
return "模拟数据库删除";
}
@Cacheable
@Override
public String select(String typeId) {
System.out.println("select()执行了=============");
return "模拟数据库查询";
}
/**
* 复杂的缓存:高速缓存
* @param name
* @return
*/
@Caching(
cacheable = {
@Cacheable(cacheNames = {"users"},key="#name") //(1)根据name查询user
},
put = {
@CachePut(cacheNames = {"users"},key="#result.id") //(2) 根据id查询user 以另一种key将查询出的结果缓存到缓存中
}
)
@Override
public UserEntity selectByName(String name) {
UserEntity userEntity = new UserEntity();
userEntity.setId(1);
userEntity.setName(name);
userEntity.setAge(23);
System.out.println("进入方法:"+name);
return userEntity;
}
}
网友评论