- 之前有写过springboot+redis实战,这里不做redis缓存测试了
-
下方有springboot+ehcache的测试用例
目录:
应用场景:
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依赖:
org.springframework.boot
spring-boot-starter-cache
net.sf.ehcache
ehcache
具体的pom文件
org.springframework.boot
spring-boot-starter-web
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.springframework
spring-tx
5.1.8.RELEASE
io.swagger
swagger-models
io.swagger
swagger-annotations
io.springfox
springfox-swagger-ui
io.springfox
springfox-swagger2
io.swagger
swagger-models
io.swagger
swagger-annotations
org.springframework.boot
spring-boot-starter-cache
net.sf.ehcache
ehcache
2、创建ehcache.xml文件
在resources下面创建ehcache.xml,复制以下内容。
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 [email protected]
* @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 [email protected]
* @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 [email protected]
* @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;
}
}