参考文档:

Spring Boot StringRedisTemplate常用操作

1. Redis 基础知识

  • Redis 是一款高性能的 NOSQL 系列的非关系型数据库。
  • NoSQL (Not Only SQL),意即“不仅仅是 SQL”,是一项全新的数据库理念,泛指非关系型的数据库。
  • NOSQL 和关系型数据库比较
  • 优点:
    • 成本:nosql 数据库简单易部署,基本都是开源软件,不需要像使用 Oracle 那样花费大量成本购买使用,相比关系型数据库价格便宜。
    • 查询速度:nosql 数据库将数据存储于缓存之中,关系型数据库将数据存储在硬盘中,自然查询速度远不及 nosql 数据库。
    • 存储数据的格式:nosql 的存储格式是 key,value 形式、文档形式、图片形式等等,所以可以存储基础类型以及对象或者是集合等各种格式,而数据库则只支持基础类型。
    • 扩展性:关系型数据库有类似 join 这样的多表查询机制的限制导致扩展很艰难。
  • 缺点:
    • 维护的工具和资料有限。
    • 不提供对 SQL 的支持,如果不支持 SQL 这样的工业标准,将产生一定用户的学习和使用成本。
    • 不提供关系型数据库对事务的处理。
  • 非关系型数据库的优势:
    • 性能 NOSQL 是基于键值对的,可以想象成表中的主键和值的对应关系,而且不需要经过 SQL 层的解析,所以性能非常高。
    • 可扩展性同样也是因为基于键值对,数据之间没有耦合性,所以非常容易水平扩展。
  • 关系型数据库的优势:
    • 复杂查询可以用 SQL 语句方便的在一个表以及多个表之间做非常复杂的数据查询。
    • 事务支持使得对于安全性能很高的数据访问要求得以实现。对于这两类数据库,对方的优势就是自己的弱势,反之亦然。
  • 总结:关系型数据库与 NoSQL 数据库并非对立而是互补的关系,即通常情况下使用关系型数据库,在适合使用 NoSQL 的时候使用 NoSQL 数据库,让 NoSQL 数据库对关系型数据库的不足进行弥补。一般会将数据存储在关系型数据库中,在 NoSQL 数据库中备份存储关系型数据库的数据。
  • Redis 是用 C 语言开发的一个开源的高性能键值对(key-value)数据库,官方提供测试数据,50 个并发执行 100000 个请求,读的速度是 110000 次/s,写的速度是 81000 次/s ,且 Redis 通过提供多种键值数据类型来适应不同场景下的存储需求,目前为止 Redis 支持的键值数据类型如下:
    • 字符串类型 string
    • 哈希类型 hash
    • 列表类型 list
    • 集合类型 set
    • 有序集合类型 sortedset
  • Redis 的应用场景
    • 缓存(数据查询、短连接、新闻内容、商品内容等等)
    • 聊天室的在线好友列表
    • 任务队列。(秒杀、抢购、12306 等等)
    • 应用排行榜
    • 网站访问统计
    • 数据过期处理(可以精确到毫秒)
    • 分布式集群架构中的 session 分离

2. 主流的 NOSQL 产品

  • 键值(Key-Value)存储数据库
    • 相关产品: Tokyo Cabinet/Tyrant、Redis、Voldemort、Berkeley DB
    • 典型应用: 内容缓存,主要用于处理大量数据的高访问负载。
    • 数据模型: 一系列键值对
    • 优势: 快速查询
    • 劣势: 存储的数据缺少结构化
  • 列存储数据库
    • 相关产品:Cassandra, HBase, Riak
    • 典型应用:分布式的文件系统
    • 数据模型:以列簇式存储,将同一列数据存在一起
    • 优势:查找速度快,可扩展性强,更容易进行分布式扩展
    • 劣势:功能相对局限
  • 文档型数据库
    • 相关产品:CouchDB、MongoDB
    • 典型应用:Web 应用(与 Key-Value 类似,Value 是结构化的)
    • 数据模型: 一系列键值对
    • 优势:数据结构要求不严格
    • 劣势: 查询性能不高,而且缺乏统一的查询语法
  • 图形(Graph)数据库
    • 相关数据库:Neo4J、InfoGrid、Infinite Graph
    • 典型应用:社交网络
    • 数据模型:图结构
    • 优势:利用图结构相关算法。
    • 劣势:需要对整个图做计算才能得出结果,不容易做分布式的集群方案。

3. Redis 命令操作

  • Redis 存储的是:key,value 格式的数据,其中 key 都是字符串,value 有 5 种不同的数据结构

    • 字符串类型 string
    • 哈希类型 hash:map 格式
    • 列表类型 list:linkedlist 格式。支持重复元素
    • 集合类型 set:不允许重复元素
    • 有序集合类型 sortedset:不允许重复元素,且元素有顺序

  • 字符串类型 string

    • 存储:set key value
      • set username zhangsan
    • 拉取:get key
      • get username
    • 删除:del key
      • del username
  • 哈希类型 hash

    • 存储:hset key field value
      • hset myhash username lisi
      • hset myhash password 123
    • 获取:
      • hget key field: 获取指定的 field 对应的值
        • hget myhash username
      • hgetall key:获取所有的 field 和 value
        • hgetall myhash
    • 删除 hash 中值:hdel key field [field...]
      • hdel myhash username
    • 删除整个 hash:del key
      • del myhash
  • 列表类型:list:可以添加一个元素到列表的头部(左边)或者尾部(右边)

    • 存储:
      • lpush key value [value...]:将元素加入列表左表
        • lpush myList a
      • rpush key value [value...]:将元素加入列表右边
        • rpush myList c
    • 获取:
      • lrange key start end:范围获取
        • lrange myList 0 -1 代表获取所有
    • 删除
      • lpop key: 删除列表最左边的元素,并将元素返回
      • rpop key: 删除列表最右边的元素,并将元素返回
  • 集合类型 set : 不允许重复元素

    • 存储:sadd key member [member...]
    • 获取:smembers key:获取 set 集合中所有元素
    • 删除:srem key member [member...]:删除 set 集合中的某个元素
  • 有序集合类型 sortedset:不允许重复元素,且元素有顺序。每个元素都会关联一个 double 类型的分数。Redis 正是通过分数来为集合中的成员进行从小到大的排序。

    • 存储:zadd key score member [score member...]
    • 获取:zrange key start end [withscores] withscores 代表是否包含分数
    • 删除:zrem key member
  • 通用命令

    • keys *:查询所有的键
    • type key:获取键对应的 value 的类型
    • del key [key...]:删除指定的 key 和所对应的 value

4. Redis 持久化

  • Redis 是一个内存数据库,当 Redis 服务器重启,获取电脑重启,数据会丢失,我们可以将 Redis 内存中的数据持久化保存到硬盘的文件中。
  • RDB 持久化机制:默认方式,不需要进行配置,默认就使用这种机制
    • 在一定的间隔时间中,检测 key 的变化情况,然后持久化数据
    • 在 redis.windwos.conf 文件中进行配置
    • #  900秒内,如果至少1个keys发生改变,则持久化一次
      save 900 1
      #  300秒内,如果至少10个keys发生改变,则持久化一次
      save 300 10
      #  60秒内,如果至少10000个keys发生改变,则持久化一次
      save 60 10000
      
    • 修改完配置以后,重启服务器。redis-server.exe redis.windows.conf 表示有某个配置文件启动服务器。
  • AOF:日志记录的方式,可以记录每一条命令的操作。可以每一次命令操作后,持久化数据,效率低。
    • 配置文件 redis.windwos.conf
      appendonly no(关闭aof) --> appendonly yes (开启aof)
      # appendfsync always : 每一次操作都进行持久化
      appendfsync everysec : 每隔一秒进行一次持久化
      # appendfsync no	 : 不进行持久化
      

5. Java 客户端:Jedis

  • Jedis: 一款 Java 操作 Redis 数据库的工具。
  • 使用步骤:
    //1. 获取连接
    //如果不传参数,默认为localhost和6379端口
    Jedis jedis = new Jedis("localhost",6379);
    //2. 操作
    jedis.set("username","zhangsan");
    //3. 关闭连接
    jedis.close();
    
  • 操作 string 类型
    • String set(String key, String value); 返回 OK
    • String get(String key); 返回 value
    • String setex(String key,int seconds,String value); 设置键值对并指定过期时间,返回 OK
  • 操作 hash 类型
    • Long hset(String key,String field,String value); 返回成功了几行
    • String hget(String key,String field); 返回 value
    • Map<String,String> hgetall(String key);
    • Long hdel(String key,String... fields); 返回成功了几行
  • 操作 list 类型
    • Long lpush/rpush(String key,String strings);
    • String lpop/rpop(String key);
    • List<String> lrange(String key,long start,long end);
  • 操作 set 类型
    • Long sadd(String key,String... members);
    • Set<String> smembers(String key); 获取所有值
    • Long srem(String key,String... members);
  • 操作 sortedset 类型
    • Long zadd(String key,double score,String member);
    • Set<String> zrange(String key,long begin,long end);
    • Long zrem(String key,String... members);
  • Jedis 连接池:JedisPool
    • //0.创建一个配置对象(可省略)
      JedisPoolConfig config = new JedisPoolConfig();
      config.setMaxTotal(50);
      config.setMaxIdle(10);
      
      //1.创建Jedis连接池对象
      JedisPool jedisPool = new JedisPool(config,"localhost",6379);
      //2.获取连接
      Jedis jedis = jedisPool.getResource();
      //3. 使用
      jedis.set("hehe","heihei");
      //4. 关闭 归还到连接池中
      jedis.close();
      
    • JedisPoolUtils
      package pers.ylq.util;
      
      import redis.clients.jedis.Jedis;
      import redis.clients.jedis.JedisPool;
      import redis.clients.jedis.JedisPoolConfig;
      
      import java.io.IOException;
      import java.io.InputStream;
      import java.util.Properties;
      
      /**
       JedisPool工具类
          加载配置文件,配置连接池的参数
          提供获取连接的方法
       */
      public class JedisPoolUtils {
      
          private static JedisPool jedisPool;
      
          static{
              //读取配置文件
              InputStream is = JedisPoolUtils.class.getClassLoader().getResourceAsStream("jedis.properties");
              //创建Properties对象
              Properties pro = new Properties();
              //关联文件
              try {
                  pro.load(is);
              } catch (IOException e) {
                  e.printStackTrace();
              }
              //获取数据,设置到JedisPoolConfig中
              JedisPoolConfig config = new JedisPoolConfig();
              config.setMaxTotal(Integer.parseInt(pro.getProperty("maxTotal")));
              config.setMaxIdle(Integer.parseInt(pro.getProperty("maxIdle")));
      
              //初始化JedisPool
              jedisPool = new JedisPool(config,pro.getProperty("host"),Integer.parseInt(pro.getProperty("port")));
          }
      
          /**
           * 获取连接方法
           */
          public static Jedis getJedis(){
              return jedisPool.getResource();
          }
      }
      

6. Redis 使用样例

  • public class ProvinceServiceImpl implements ProvinceService {
        //声明dao
        private ProvinceDao dao = new ProvinceDaoImpl();
        /**
            使用redis缓存
         */
        @Override
        public String findAllJson() {
            //1.先从redis中查询数据
            //1.1获取redis客户端连接
            Jedis jedis = JedisPoolUtils.getJedis();
            String province_json = jedis.get("province");
            //2判断 province_json 数据是否为null
            if(province_json == null || province_json.length() == 0){
                //redis中没有数据
                System.out.println("redis中没数据,查询数据库...");
                //2.1从数据中查询
                List<Province> ps = dao.findAll();
                //2.2将list序列化为json
                ObjectMapper mapper = new ObjectMapper();
                try {
                    province_json = mapper.writeValueAsString(ps);
                } catch (JsonProcessingException e) {
                    e.printStackTrace();
                }
                //2.3 将json数据存入redis
                jedis.set("province",province_json);
                //归还连接
                jedis.close();
            }else{
                System.out.println("redis中有数据,查询缓存...");
            }
            return province_json;
        }
    }
    

7. StringRedisTemplate

我们在 spring boot 中集成 redis

Redis 文档中介绍存入或查询可以使 Object 类型,但是实际中用的是 String 类型

POM 依赖

<!--redis依赖配置-->
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置

spring:
  redis:
    host: localhost  # Redis服务器地址
    database: 0 # Redis数据库索引(默认为0)
    port: 6379 # Redis服务器连接端口
    password: # Redis服务器连接密码(默认为空)
    jedis:
      pool:
        max-active: 8 # 连接池最大连接数(使用负值表示没有限制)
        max-wait: -1ms # 连接池最大阻塞等待时间(使用负值表示没有限制)
        max-idle: 8 # 连接池中的最大空闲连接
        min-idle: 0 # 连接池中的最小空闲连接
    timeout: 3000ms # 连接超时时间(毫秒)

注入对象

@Autowired
StringRedisTemplate stringRedisTemplate;

Redis Value

@Test
public void valueAddResitTest(){
    stringRedisTemplate.opsForValue().set("key","value");
}

@Test
public void valueGetResitTest(){
    String value = stringRedisTemplate.opsForValue().get("key");
    logger.info("value:{}", value);
}

@Test
public void valueDelResitTest(){
    stringRedisTemplate.delete("key");
}

@Test
public void valueTimeoutResitTest(){
    stringRedisTemplate.opsForValue().set("timeStep", new Date().getTime()+"", 2 ,TimeUnit.MINUTES);
}

Redis List

// list数据类型适合于消息队列的场景:比如12306并发量太高,而同一时间段内只能处理指定数量的数据!必须满足先进先出的原则,其余数据处于等待
@Test
public void listPushResitTest(){
    // leftPush依次由右边添加
    stringRedisTemplate.opsForList().rightPush("myList","1");
    stringRedisTemplate.opsForList().rightPush("myList","2");
    stringRedisTemplate.opsForList().rightPush("myList", "A");
    stringRedisTemplate.opsForList().rightPush("myList", "B");
    // leftPush依次由左边添加
    stringRedisTemplate.opsForList().leftPush("myList", "0");
}

@Test
public void listGetListResitTest(){
    // 查询类别所有元素
    List<String> listAll = stringRedisTemplate.opsForList().range( "myList", 0, -1);
    logger.info("list all {}", listAll);
    // 查询前3个元素
    List<String> list = stringRedisTemplate.opsForList().range( "myList", 0, 3);
    logger.info("list limit {}", list);
}

@Test
public void listRemoveOneResitTest(){
    // 删除先进入的B元素
    stringRedisTemplate.opsForList().remove("myList",1, "B");
}

@Test
public void listRemoveAllResitTest(){
    // 删除所有A元素
    stringRedisTemplate.opsForList().remove("myList",0, "A");
}

Redis Hash

@Test
public void hashPutResitTest(){
    // map的key值相同,后添加的覆盖原有的
    stringRedisTemplate.opsForHash().put("banks:12600000", "a", "b");
}

@Test
public void hashGetEntiresResitTest(){
    // 获取map对象
    Map<Object, Object> map = stringRedisTemplate.opsForHash().entries("banks:12600000");
    logger.info("objects:{}", map);
}

@Test
public void hashGeDeleteResitTest(){
    // 根据map的key删除这个元素
    stringRedisTemplate.opsForHash().delete("banks:12600000", "c");
}

@Test
public void hashGetKeysResitTest(){
    // 获得map的key集合
    Set<Object> objects =  stringRedisTemplate.opsForHash().keys("banks:12600000");
    logger.info("objects:{}", objects);
}

@Test
public void hashGetValueListResitTest(){
    // 获得map的value列表
    List<Object> objects = stringRedisTemplate.opsForHash().values("banks:12600000");
    logger.info("objects:{}", objects);
}

@Test
public void hashSize() {
    // 获取map对象大小
    long size =  stringRedisTemplate.opsForHash().size("banks:12600000");
    logger.info("size:{}", size);
}

8. SpringBoot 整合 redis

参考文献:mall整合Redis实现缓存功能

pom 依赖和 redis 配置参考 StringRedisTemplate

添加 Redis 的自定义 key 配置

# 自定义redis key
redis:
  key:
    prefix:
      authCode: "portal:authCode:"
    expire:
      authCode: 120 # 验证码超期时间

添加 RedisService 接口用于定义一些常用 Redis 操作

package com.macro.mall.tiny.service;

/**
 * redis操作Service,
 * 对象和数组都以json形式进行存储
 * Created by macro on 2018/8/7.
 */
public interface RedisService {
    /**
     * 存储数据
     */
    void set(String key, String value);

    /**
     * 获取数据
     */
    String get(String key);

    /**
     * 设置超期时间
     */
    boolean expire(String key, long expire);

    /**
     * 删除数据
     */
    void remove(String key);

    /**
     * 自增操作
     * @param delta 自增步长
     */
    Long increment(String key, long delta);

}

注入 StringRedisTemplate,实现 RedisService 接口

package com.macro.mall.tiny.service.impl;

import com.macro.mall.tiny.service.RedisService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.concurrent.TimeUnit;

/**
 * redis操作Service的实现类
 * Created by macro on 2018/8/7.
 */
@Service
public class RedisServiceImpl implements RedisService {
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public void set(String key, String value) {
        stringRedisTemplate.opsForValue().set(key, value);
    }

    @Override
    public String get(String key) {
        return stringRedisTemplate.opsForValue().get(key);
    }

    @Override
    public boolean expire(String key, long expire) {
        return stringRedisTemplate.expire(key, expire, TimeUnit.SECONDS);
    }

    @Override
    public void remove(String key) {
        stringRedisTemplate.delete(key);
    }

    @Override
    public Long increment(String key, long delta) {
        return stringRedisTemplate.opsForValue().increment(key,delta);
    }
}

然后在 Controller 调用即可

9. Spring Data Redis

参考文献:Spring Data Redis 最佳实践!

Spring Data Redis 是Spring 框架提供的用于操作Redis的方式

9.1 Spring Cache 操作 redis

当Spring Boot 结合Redis来作为缓存使用时,最简单的方式就是使用Spring Cache了,使用它我们无需知道Spring中对Redis的各种操作,仅仅通过它提供的@Cacheable 、@CachePut 、@CacheEvict 、@EnableCaching等注解就可以实现缓存功能。

9.1.1 常用注解

@EnableCaching

开启缓存功能,一般放在启动类上。

@Cacheable

使用该注解的方法当缓存存在时,会从缓存中获取数据而不执行方法,当缓存不存在时,会执行方法并把返回结果存入缓存中。一般使用在查询方法上,可以设置如下属性:

  • value:缓存名称(必填),指定缓存的命名空间;
  • key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义;
  • unless:条件符合则不缓存;
  • condition:条件符合则缓存。

其实在数据库中存储的是一个 hash 类型,比如

@Cacheable(value = "myvalue", key = "#param")
public Integer methodB(String param) {
    return 111;
}
// 则存储的 key 为 myvalue::param

@Cacheable("myvalue")
// 若缺失 key, 则缺省按照方法的所有参数进行组合

@CachePut

使用该注解的方法每次执行时都会把返回结果存入缓存中。一般使用在新增方法上,可以设置如下属性:

  • value:缓存名称(必填),指定缓存的命名空间;
  • key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义;
  • unless:条件符合则不缓存;
  • condition:条件符合则缓存。

@CacheEvict

使用该注解的方法执行时会清空指定的缓存。一般使用在更新或删除方法上,可以设置如下属性:

  • value:缓存名称(必填),指定缓存的命名空间;
  • key:用于设置在命名空间中的缓存key值,可以使用SpEL表达式定义;
  • condition:条件符合则缓存。

9.1.2 使用步骤

添加依赖

<!--redis依赖配置-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置 redis

spring:
  redis:
    host: 192.168.6.139 # Redis服务器地址
    database: 0 # Redis数据库索引(默认为0)
    port: 6379 # Redis服务器连接端口
    password: # Redis服务器连接密码(默认为空)
    timeout: 1000ms # 连接超时时间

添加配置类

此时我们就会想到有没有什么办法让 Redis 中存储的数据变成标准的 JSON 格式,然后可以设置一定的过期时间,不设置过期时间容易产生很多不必要的缓存数据。

我们可以通过给 RedisTemplate 设置 JSON 格式的序列化器,并通过配置 RedisCacheConfiguration 设置超时时间来实现以上需求,具体配置类 RedisConfig 代码如下;

/**
 * Redis配置类
 * Created by macro on 2020/3/2.
 */
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {

    /**
     * redis数据库自定义key
     */
    public  static final String REDIS_KEY_DATABASE="mall";

    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisSerializer<Object> serializer = redisSerializer();
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(serializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(serializer);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public RedisSerializer<Object> redisSerializer() {
        //创建JSON序列化器
        Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        serializer.setObjectMapper(objectMapper);
        return serializer;
    }

    @Bean
    public RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory) {
        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory);
        //设置Redis缓存有效期为1天
        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer())).entryTtl(Duration.ofDays(1));
        return new RedisCacheManager(redisCacheWriter, redisCacheConfiguration);
    }

}

Service 类

@Service
public class PmsBrandServiceImpl implements PmsBrandService {
    @Autowired
    private PmsBrandMapper brandMapper;

    @CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id")
    @Override
    public int update(Long id, PmsBrand brand) {
        brand.setId(id);
        return brandMapper.updateByPrimaryKeySelective(brand);
    }

    @CacheEvict(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id")
    @Override
    public int delete(Long id) {
        return brandMapper.deleteByPrimaryKey(id);
    }

    @Cacheable(value = RedisConfig.REDIS_KEY_DATABASE, key = "'pms:brand:'+#id", unless = "#result==null")
    @Override
    public PmsBrand getItem(Long id) {
        return brandMapper.selectByPrimaryKey(id);
    }

}

9.2 redis 连接池

Lettuce 是一种可伸缩,线程安全,完全非阻塞的 Redis 客户端,多个线程可以共享一个 RedisConnection,它利用 Netty NIO 框架来高效地管理多个连接,从而提供了异步和同步数据访问方式,用于构建非阻塞的反应性应用程序。

导入依赖

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

配置文件

spring:
  redis:
    lettuce:
      pool:
        max-active: 8 # 连接池最大连接数
        max-idle: 8 # 连接池最大空闲连接数
        min-idle: 0 # 连接池最小空闲连接数
        max-wait: -1ms # 连接池最大阻塞等待时间,负值表示没有限制

9.3 RedisTemplate

Spring Cache 给我们提供了操作 Redis 缓存的便捷方法,但是也有很多局限性。比如说我们想单独设置一个缓存值的有效期怎么办?我们并不想缓存方法的返回值,我们想缓存方法中产生的中间值怎么办?此时我们就需要用到 RedisTemplate 这个类了,接下来我们来讲下如何通过 RedisTemplate 来自由操作 Redis 中的缓存。

RedisService

/**
 * redis 操作 Service
 */
public interface RedisService {

    /**
     * 保存属性
     */
    void set(String key, Object value, long time);

    /**
     * 保存属性
     */
    void set(String key, Object value);

    /**
     * 获取属性
     */
    Object get(String key);

    /**
     * 删除属性
     */
    Boolean del(String key);

    /**
     * 批量删除属性
     */
    Long del(List<String> keys);

    /**
     * 设置过期时间
     */
    Boolean expire(String key, long time);

    /**
     * 获取过期时间
     */
    Long getExpire(String key);

    /**
     * 判断是否有该属性
     */
    Boolean hasKey(String key);

    /**
     * 按delta递增
     */
    Long incr(String key, long delta);

    /**
     * 按delta递减
     */
    Long decr(String key, long delta);

    /**
     * 获取Hash结构中的属性
     */
    Object hGet(String key, String hashKey);

    /**
     * 向Hash结构中放入一个属性
     */
    Boolean hSet(String key, String hashKey, Object value, long time);

    /**
     * 向Hash结构中放入一个属性
     */
    void hSet(String key, String hashKey, Object value);

    /**
     * 直接获取整个Hash结构
     */
    Map<Object, Object> hGetAll(String key);

    /**
     * 直接设置整个Hash结构
     */
    Boolean hSetAll(String key, Map<String, Object> map, long time);

    /**
     * 直接设置整个Hash结构
     */
    void hSetAll(String key, Map<String, Object> map);

    /**
     * 删除Hash结构中的属性
     */
    void hDel(String key, Object... hashKey);

    /**
     * 判断Hash结构中是否有该属性
     */
    Boolean hHasKey(String key, String hashKey);

    /**
     * Hash结构中属性递增
     */
    Long hIncr(String key, String hashKey, Long delta);

    /**
     * Hash结构中属性递减
     */
    Long hDecr(String key, String hashKey, Long delta);

    /**
     * 获取Set结构
     */
    Set<Object> sMembers(String key);

    /**
     * 向Set结构中添加属性
     */
    Long sAdd(String key, Object... values);

    /**
     * 向Set结构中添加属性
     */
    Long sAdd(String key, long time, Object... values);

    /**
     * 是否为Set中的属性
     */
    Boolean sIsMember(String key, Object value);

    /**
     * 获取Set结构的长度
     */
    Long sSize(String key);

    /**
     * 删除Set结构中的属性
     */
    Long sRemove(String key, Object... values);

    /**
     * 获取List结构中的属性
     */
    List<Object> lRange(String key, long start, long end);

    /**
     * 获取List结构的长度
     */
    Long lSize(String key);

    /**
     * 根据索引获取List中的属性
     */
    Object lIndex(String key, long index);

    /**
     * 向List结构中添加属性
     */
    Long lPush(String key, Object value);

    /**
     * 向List结构中添加属性
     */
    Long lPush(String key, Object value, long time);

    /**
     * 向List结构中批量添加属性
     */
    Long lPushAll(String key, Object... values);

    /**
     * 向List结构中批量添加属性
     */
    Long lPushAll(String key, Long time, Object... values);

    /**
     * 从List结构中移除属性
     */
    Long lRemove(String key, long count, Object value);
}

RedisServiceImpl

/**
 * redis操作实现类
 */
@Service
public class RedisServiceImpl implements RedisService {
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Override
    public void set(String key, Object value, long time) {
        redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
    }

    @Override
    public void set(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }

    @Override
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }

    @Override
    public Boolean del(String key) {
        return redisTemplate.delete(key);
    }

    @Override
    public Long del(List<String> keys) {
        return redisTemplate.delete(keys);
    }

    @Override
    public Boolean expire(String key, long time) {
        return redisTemplate.expire(key, time, TimeUnit.SECONDS);
    }

    @Override
    public Long getExpire(String key) {
        return redisTemplate.getExpire(key, TimeUnit.SECONDS);
    }

    @Override
    public Boolean hasKey(String key) {
        return redisTemplate.hasKey(key);
    }

    @Override
    public Long incr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, delta);
    }

    @Override
    public Long decr(String key, long delta) {
        return redisTemplate.opsForValue().increment(key, -delta);
    }

    @Override
    public Object hGet(String key, String hashKey) {
        return redisTemplate.opsForHash().get(key, hashKey);
    }

    @Override
    public Boolean hSet(String key, String hashKey, Object value, long time) {
        redisTemplate.opsForHash().put(key, hashKey, value);
        return expire(key, time);
    }

    @Override
    public void hSet(String key, String hashKey, Object value) {
        redisTemplate.opsForHash().put(key, hashKey, value);
    }

    @Override
    public Map<Object, Object> hGetAll(String key) {
        return redisTemplate.opsForHash().entries(key);
    }

    @Override
    public Boolean hSetAll(String key, Map<String, Object> map, long time) {
        redisTemplate.opsForHash().putAll(key, map);
        return expire(key, time);
    }

    @Override
    public void hSetAll(String key, Map<String, Object> map) {
        redisTemplate.opsForHash().putAll(key, map);
    }

    @Override
    public void hDel(String key, Object... hashKey) {
        redisTemplate.opsForHash().delete(key, hashKey);
    }

    @Override
    public Boolean hHasKey(String key, String hashKey) {
        return redisTemplate.opsForHash().hasKey(key, hashKey);
    }

    @Override
    public Long hIncr(String key, String hashKey, Long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, delta);
    }

    @Override
    public Long hDecr(String key, String hashKey, Long delta) {
        return redisTemplate.opsForHash().increment(key, hashKey, -delta);
    }

    @Override
    public Set<Object> sMembers(String key) {
        return redisTemplate.opsForSet().members(key);
    }

    @Override
    public Long sAdd(String key, Object... values) {
        return redisTemplate.opsForSet().add(key, values);
    }

    @Override
    public Long sAdd(String key, long time, Object... values) {
        Long count = redisTemplate.opsForSet().add(key, values);
        expire(key, time);
        return count;
    }

    @Override
    public Boolean sIsMember(String key, Object value) {
        return redisTemplate.opsForSet().isMember(key, value);
    }

    @Override
    public Long sSize(String key) {
        return redisTemplate.opsForSet().size(key);
    }

    @Override
    public Long sRemove(String key, Object... values) {
        return redisTemplate.opsForSet().remove(key, values);
    }

    @Override
    public List<Object> lRange(String key, long start, long end) {
        return redisTemplate.opsForList().range(key, start, end);
    }

    @Override
    public Long lSize(String key) {
        return redisTemplate.opsForList().size(key);
    }

    @Override
    public Object lIndex(String key, long index) {
        return redisTemplate.opsForList().index(key, index);
    }

    @Override
    public Long lPush(String key, Object value) {
        return redisTemplate.opsForList().rightPush(key, value);
    }

    @Override
    public Long lPush(String key, Object value, long time) {
        Long index = redisTemplate.opsForList().rightPush(key, value);
        expire(key, time);
        return index;
    }

    @Override
    public Long lPushAll(String key, Object... values) {
        return redisTemplate.opsForList().rightPushAll(key, values);
    }

    @Override
    public Long lPushAll(String key, Long time, Object... values) {
        Long count = redisTemplate.opsForList().rightPushAll(key, values);
        expire(key, time);
        return count;
    }

    @Override
    public Long lRemove(String key, long count, Object value) {
        return redisTemplate.opsForList().remove(key, count, value);
    }
}

10. Redis 集群

redis 集群

11. Redis 常见问题

redis 常见问题