Spring Cache\Redis\Memcached

Spring Cache

1.简介

核心思想是当在调用一个缓存方法时,会把该方法参数和返回结果作为一个键值对存放在缓存中,等到下一次利用同样的参数来调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回,从而实现缓存的功能。

1.@Cacheable

用于标记缓存,也就是对使用@Cacheable注解的位置进行缓存。@Cacheable可以在方法或者类上进行标记,当对方法进行标记时,表示此方法支持缓存;当对此类进行标记时,表明当前类中的所有方法都支持缓存。在支持Spring Cache的环境下,对于使用@Cacheable标记的方法,Spring在每次调用方法前都会根据key查询当前Cache中是否存在相同的key的缓存元素,如果存在,就不再执行该方法,而是直接从缓存中获取结果进行返回,否则执行该方法并将返回结果存入指定的缓存中。他有3个属性:

  • value:在使用@Cacheable注解的时候,value属性是必须要指定的,这个属性用于指定Cache的名称,也就是说,表明当前缓存的返回值用于哪个缓存上。
  • key:用于指定缓存对应的key。key属性不是必须指定的,如果没有指定key,Spring就会为我们使用默认策略生成对应的key。默认策略规定:当前缓存方法没有参数,那么当前key为0;当前缓存方法有一个参数,那么以key为参数值;当前缓存方法有多个参数,那么key为所有参数的hashcode值。当然,也可以用Spring提供的EL表达式来指定当前缓存方法的key。通常来说,我们可以使用当前缓存方法的参数指定key,一般为“#参数名”。如果参数为对象,就可以使用对象的属性指定key。
    @Cacheable(value="users",key="#users.id")
    public User findUser(User user){
        return new User();
    }

     

  • condition:主要用于指定当前缓存的触发条件。
    @Cacheable(value="users",key="#users.id",condition="#user.id%2==0")
    public User findUser(User user){
        return new User();
    }

     

2.@CachePut

@CahePut只是用于将标记该注解的方法返回值放入缓存中,无论缓存中是否包含当前缓存,只是以键值的形式将执行结果放入缓存中。在使用方面,@CachePut注解和@Cacheable注解一致。

3.@CacheEvict

@CacheEvict注解用于清除缓存数据,与@Cacheable类似,不过@CacheEvict用于方法是清除当前方法的缓存,用于类时清除当前类所有方法的缓存。@CacheEvict除了提供与@Cacheable一致的3个属性外,还提供了一个常用的属性allEntries,这个属性的默认值为false,如果指定属性值为true,就会清除当前value值所有的缓存。

2.配置Spring Cache依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

3.测试

在启动类上加入@EnableCaching注解,表示启动缓存

package com.springboot.controller;
//imports

@RestController
public class UserController {

    @Autowired
    private UserRepository userRepository;

    @GetMapping("/saveUser")
    @CachePut(value = "user", key = "#id")
    public User saveUser(Long id, String userName, String userPassword){
        User user = new User(id,userName, userPassword);
        userRepository.save(user);
        return user;
    }

    @GetMapping("/queryUser")
    @Cacheable(value = "user", key = "#id")
    public Optional<User> queryUser(Long id){
        return userRepository.findById(id);
    }

    @GetMapping("/deleteUser")
    @CacheEvict(value = "user", key = "#id")
    public String deleteUser(Long id){
        userRepository.deleteById(id);
        return "success";
    }

    @GetMapping("/deleteCache")
    @CacheEvict(value = "user", allEntries = true)
    public void deleteCache() {
    }
}
  1. saveUser方法,其中使用到@CachePut,将返回值存放在缓存中
  2. queryUser方法,使用到@Cacheable,这个注解在执行前查看是否已经存在缓存,如果存在,这直接返回;如果不存在,就将返回值存入缓存后再返回
  3. deleteUser方法,使用到@CacheEvict,用于删除对应的缓存
  4. deleteCache方法,用于删除所有value为user的缓存

 

Redis

1.简介

Redis是一个高性能的缓存存储系统,并且以key-value的形式存储数据。目前Value支持5种数据类型,其中包括string(字符串)、list(链表)、set(集合)、zset(有序集合)、hash(哈希类型)。Redis还支持数据持久化。

2.配置

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
# Redis服务器地址
spring.redis.host=localhost
# Redis服务器连接端口
spring.redis.port=6379 

对于使用Redis,常用操作无非就是set方法和get方法。使用RedisTemplate进行存放数据和取出数据。 

redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));修改Redis里面查看key编码问题

package com.springboot.service;
//imports

@Service
public class RedisService {
    @Autowired
    private RedisTemplate redisTemplate;

    public void set(String key, Object value) {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
        ValueOperations<String, Object> vo = redisTemplate.opsForValue();
        vo.set(key, value);
    }

    public void set(String key, Object value, Long time, TimeUnit t) {
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));
        ValueOperations<String, Object> vo = redisTemplate.opsForValue();
        vo.set(key, value, time, t);
    }

    public Object get(String key) {
        ValueOperations<String, Object> vo = redisTemplate.opsForValue();
        return vo.get(key);
    }
}

3.测试

创建一个UserController进行测试,需要对实体类进行序列化,

 redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new Jackson2JsonRedisSerializer(Object.class));里面key的编码问题

package com.springboot.controller;
//imports

@RestController
public class UserController {
    @Autowired
    private RedisService redisService;

    @Autowired
    private UserRepository userRepository;

    @GetMapping(value = "saveUser")
    public String saveUser(Long id, String userName, String userPassword) {
        User user = new User(id, userName, userPassword);
        redisService.set(id.toString(), user);
        return "success";
    }

    @GetMapping(value = "getUserById")
    public Object getUserById(Long id) {
        return redisService.get(id.toString());
    }

    @GetMapping("/saveUser2")
    public User saveUser2(Long id, String userName, String userPassword) {
        User user = new User(id, userName, userPassword);
        userRepository.save(user);
        return user;
    }

    @GetMapping(value = "getUser")
    public Object getUser(Long id) {
        Object object = redisService.get(id.toString());
        if (object == null) {
            object = (userRepository.findById(id)).get();
            if (object != null) {
                redisService.set(id.toString(), object, 100L, TimeUnit.SECONDS);
            }
        }
        return object;
    }
}

Memcached

1.简介

Memcached是一个自由开源的、高性能的、分布式的内存对象缓存系统。是一种基于内存的key-value存储,用来存储小块的任意数据(字符串、对象)。这些数据可以是数据库调用、API调用或者页面渲染的结果。一般的使用目的是通过缓存数据库查询结果,减少数据库访问次数,以提高动态web应用的速度和可扩展性。

2.配置Memcached依赖

<dependency>
            <groupId>net.spy</groupId>
            <artifactId>spymemcached</artifactId>
            <version>2.12.2</version>
        </dependency>
memcache.ip=localhost
memcache.port=11211

配置MemcachedConfig,并且封装一些Memcached常用的方法

package com.springboot.config;
//imports

@Component
public class MemcachedConfig implements CommandLineRunner {
    private Logger logger =  LoggerFactory.getLogger(this.getClass());

    @Value("${memcache.ip}")
    private String memcacheIp;

    @Value("${memcache.port}")
    private Integer memcachePort;

    private MemcachedClient client = null;

    @Override
    public void run(String... args) throws Exception {
        try {
            client = new MemcachedClient(new InetSocketAddress(memcacheIp,memcachePort));
        } catch (IOException e) {
            logger.error("Connection to server failed",e);
        }
        logger.info("Connection to server success");
    }

    public MemcachedClient getClient() {
        return client;
    }


    public Boolean set(String key,int time,String value) {
        Boolean b = false;
        try{
            b=(this.getClient().set(key, time, value)).get();
        }catch (Exception e){
            logger.error(e.getMessage());
        }
        return b;
    }

    public Boolean add(String key,int time,String value){
        Boolean b = false;
        try{
            b=(this.getClient().add(key, time, value)).get();
        }catch (Exception e){
            logger.error(e.getMessage());
        }
        return b;
    }

    public Object replace(String key,int time,String value){
        Boolean b = false;
        try{
            b=(this.getClient().replace(key, time, value)).get();
        }catch (Exception e){
            logger.error(e.getMessage());
        }
        return b;
    }

    public Object append(String key,int time,String value){
        Boolean b = false;
        try{
            b=(this.getClient().append(key, value)).get();
        }catch (Exception e){
            logger.error(e.getMessage());
        }
        return b;
    }

    public Object prepend(String key,int time,String value){
        Boolean b = false;
        try{
            b=(this.getClient().prepend(key, value)).get();
        }catch (Exception e){
            logger.error(e.getMessage());
        }
        return b;
    }

    public Object cas(String key,int time,String value){
        return this.getClient().cas(key, time, value);
    }

    public Object get(String key){
        return this.getClient().get(key);
    }

    public Boolean delete(String key){
        Boolean b = false;
        try{
            b=(this.getClient().delete(key)).get();
        }catch (Exception e){
            logger.error(e.getMessage());
        }
        return b;
    }

    public long incr(String key,Integer value){
        return this.getClient().incr(key,value);
    }

    public long decr(String key,Integer value){
        return this.getClient().decr(key,value);
    }

}
  • set方法:当前方法用于将value存储在指定的key中,并可以设置对应的过期时间。如果当前key存在值,就更新value;如果不存在或已经过期,就存储对应的数据值。
  • add方法:当前方法用于将value存储在指定的key中,如果add的可以已经存在,就不会更新数据(过期的key会更新),之前的值将仍然保持相同,并且将获得响应NOT_STORED。
  • replace方法:的其方法用于替换已经存在的key的value。如果key不存在替换将会失败,并且你将获得响应NOT_STORED
  • append方法:当前方法用于向已存在key的value后面追加数据。
  • prepend方法:当前方法用于向已存在key的value前面追加数据。
  • cas方法:当前方法用于执行一个“检查并设置”的操作,他仅在当前客户端最后一次取值后,该key对应的值没有被其他客户端修改的情况下才能够将值写入。检查是通过cas_token参数进行的,这个参数是Memcached指定给已经存在的元素的唯一的64位值。
  • get方法:当前方法用于获取存储在key中的value,如果key不存在或者已经过期,就返回为空。
  • gets方法:当前方法用于获取带有CAS令牌存储的value,如果key不存在,就返回空。
  • delete方法:当前方法用于删除已存在的key。
  • incr方法:当前方法用于对已存在的key的数字值进行自增操作。如果key不存在就返回NOT_FOUND;如果键的值不为数字,就返回CLIENT_ERROR;其他错误返回ERROR。
  • decr方法:当前方法用于对已存在的key的数字值进行自减操作。如果key不存在就返回NOT_FOUND;如果键的值不为数字,就返回CLIENT_ERROR;其他错误返回ERROR。
package com.springboot.controller;
//imports

@RestController
public class UserController {

    @Resource
    private MemcachedConfig memcachedConfig;

    @GetMapping(value = "saveUser")
    public Boolean saveUser(Long id, String userName, String userPassword){
        User user = new User(id, userName, userPassword);
        return memcachedConfig.set(id.toString(), 1000,user.toString());
    }

    @GetMapping(value = "getUserById")
    public Object getUserById(Long id) {
        return memcachedConfig.get(id.toString());
    }

    @GetMapping(value = "deleteCacheById")
    public Boolean deleteCacheById(Long id) {
        return memcachedConfig.delete(id.toString());
    }

}

3.使用Memcached缓存

使用Memcached作为数据库缓存的流程其实和使用Redis缓存一致。首先查询Memcached是否含有数据,如果数据不存在或已经过期,就先从数据库查询,再插入Memcached数据已提供下次使用。

 @Autowired
    private UserRepository userRepository;

    @GetMapping("/saveUser2")
    public User saveUser2(Long id, String userName, String userPassword) {
        User user = new User(id, userName, userPassword);
        userRepository.save(user);
        return user;
    }

    @GetMapping(value = "getUserById2")
    public Object getUserById2(Long id) {
        Object object = memcachedConfig.get(id.toString());
        if (object == null) {
            object = (userRepository.findById(id)).get();
            if (object != null) {
                memcachedConfig.set(id.toString(), 1000, object.toString());
            }
        }
        return object;
    }

4.Redis和Memcached的区别

  1. 数据类型支持不同——Memcached只支持简单的key-value存储,而Redis除了支持key-value外,还支持list、set、zset、string、hashcode结构
  2. 数据一致性——Memcached内部提供了cas命令,可以保证在高并发下访问数据的一致性问题,而Redis没有提供类似cas命令,但是Redis提供了事务的功能,可以用其保证事务的原子性。
  3. value值大小——Redis的value值最大可为1GB,而Memcached只有1MB。
  4. 存储方式——Memcached将数据全部存储在内存中,当发生断电或内存分配不足时会造成数据丢失。Redis支持数据的持久化,可以将内存中的数据保存在磁盘中,重启的时候可以再次加载使用。
  5. 网络IO模型——Redis使用单线程的IO复用模型,Memcached是多线程,非阻塞IO复用的网络模型。
  6. 持久化支持——Redis提供了RDB和AOF的持久化支持,Memcached不支持持久化
  7. 应用场景——Memcached多应用于缓存数据集、临时数据、Session等、,而Redis除了可以用作缓存数据库外,还可以用作消息队列、数据堆栈等。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值