Redis 简介

Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。从2010年3月15日起,Redis的开发工作由VMware主持。从2013年5月开始,Redis的开发由Pivotal赞助。------ 来自百度百科。

Redis 常见的应用场景

1. 热点数据的存贮和展示
“热点数据”有两个较明显的特点,其一:这些数据被用户高频率的访问,其二:这些数据的内容不常发生改变。比如说一个购物网站中的商品分类、一个新闻资讯网站的导航栏目等。

2. 最近访问的数据
用户最近访问的数据在数据库中一般用时间类型的字段作为标识,常见的实现方式是通过一段时间差去数据库查询然后把符合条件的记录返回,这种解决方法的缺点是较消性能和耗时。基于Redis 中的List 数据结构来实现这功能避免了这个缺点。下文会详细介绍如何使用。

3. 并发访问
并发访问可以粗糙理解为在某一个时间段有大量的请求访问。比如在某一个时刻秒杀一个商品会并发访问“商品库存”,这会对数据库造成巨大的压力。此时引用Redis 似乎是一个好的解决方案。

4. 排行榜
在关系型数据可以基于Order by 来实现,但是Redis 有更改的解决方案—— Sorted Set 。可以解决关系型数据库的性能瓶颈。

SpringBoot 整合Redis

1. 去官网下载redis 安装到本地。
至于如何安装和启动redis比较简单此处暂省略,之前没接触过redis 的同学可以先去了解redis 的基础命令。
2. 在项目中添加依赖

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-redis</artifactId>
            <version>1.3.3.RELEASE</version>
        </dependency>

3. 在application文件配置好redis 的基本信息

spring.redis.host=127.0.0.1
spring.redis.port=6379
# redis密码默认为空,我本地redis 密码为123456
spring.redis.password=123456

4. 注入bean 组件

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

/**
 * @description: redis 配置类
 * @author: Edison
 * @create: 2020-10-18 00:01
 **/
@Configuration
public class RedisConfig {

    @Autowired
    private RedisConnectionFactory redisConnectionFactory;

    /**
     * 缓存redis-redisTemplate
     *
     * @return
     */
    @Bean
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<String, Object>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        //TODO:指定大key序列化策略为为String序列化
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
        //TODO:指定hashKey序列化策略为String序列化
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        return redisTemplate;
    }
    /**
     * 缓存redis-stringRedisTemplate
     *
     * @return
     */
    @Bean
    public StringRedisTemplate stringRedisTemplate() {
        StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
        stringRedisTemplate.setConnectionFactory(redisConnectionFactory);
        return stringRedisTemplate;
    }
}

redis 5种数据结构

数据类型

常存储的值

常用操作

String

字符串、整数或者浮点数

对整个字符串或者字符串的其中一部分执行操作对整数和浮点数执行自增或者自减操作

List

列表

从两端压入或者弹出元素对单个或者多个元素进行修剪,只保留一个范围内的元素

Set

无序集合

添加、获取、移除单个元素 检查一个元素是否存在于集合中 计算交集、并集、差集 从集合里面随机获取元素

ZSet

有序集合

添加、获取、删除元素 根据分值范围或者成员来获取元素 计算一个键的排名

Hash

包含键值对的无序散列表

添加、获取、移除单个键值对获取所有键值对检查某个键是否存在

基于RedisTemplate 操作Redis 五种数据结构

1. string(字符串)
String 数据结构应用场景很丰富,可用于计数,存贮字符串 和实体信息等。
存贮字符串 和实体信息例子如下

private static final Logger log= LoggerFactory.getLogger(RedisTest.class);

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    public User initUser(int id,String name){
        User user = new User();
        user.setId(id);
        user.setName(name);

        return user;
    }
    /**
     * redisTemplate 操作简单的String 数据结构,存储简单的String 类型数据
     */
    @Test
    public  void StringTest(){
        final String value = "this is a String";
        final String key = "redis:redisTemplate:string";
        ValueOperations valueOperations = redisTemplate.opsForValue();
        // set value
        valueOperations.set(key,value);
        //get value
        Object getValue = valueOperations.get(key);
        log.info("redis 读取出的内容:{}",getValue);
        //redis 读取出的内容:this is a String
    }

    /**
     * redisTemplate 操作String 数据结构,存储实体类信息
     * @throws Exception
     */
    @Test
    public void entityTest() throws Exception{
       ValueOperations valueOperations = redisTemplate.opsForValue();
       String key = "redis:test:entity";
       User user = initUser(1,"edison");
       // objectMapper 用于String 与 user 实体类映射
       String value = objectMapper.writeValueAsString(user);
        valueOperations.set(key,value);
        User v =  objectMapper.readValue(valueOperations.get(key).toString(),User.class);
        log.info("redis 读取出的内容:{}",v);
        // redis 读取出的内容:User(id=1, name=edison)
    }

User 实体类

import lombok.Data;
import lombok.ToString;

import java.io.Serializable;

/**
 * @description:
 * @author: Edison
 * @create: 2020-10-18 14:04
 **/
@Data
@ToString
public class User implements Serializable {
    private  int id;
    private String name;

}

2. list(列表)

有序列表,可存list 做为消息队列等;

存贮list集合以及简易消息队列的例子如下:

/**
     * redisTemplate 操作 list 数据结构
     */
    @Test
    public void list(){
        List<User> listUser = new ArrayList<>();
        listUser.add(initUser(1,"jack"));
        listUser.add(initUser(2,"lee"));
        listUser.add(initUser(3,"edison"));
        listUser.add(initUser(4,"andy"));
        listUser.add(initUser(5,"lucy"));
        log.info("listUser 中的的信息 {}",listUser);


        String key = "redis:list";
        ListOperations listOperations = redisTemplate.opsForList();

        // 为了每次单元测试运行的结果是一样的,在给这个list 添加元素之前先从缓存中删除这个list
        redisTemplate.delete(key);

        // list 是有序的 尝试从list的左边开始存贮元素
        listOperations.leftPushAll(key, listUser);
        List listUserFromRedis = redisTemplate.opsForList().range(key, 0, -1);
        log.info(" 从redis中读取list信息: {}",listUserFromRedis );
        //[User(id=5, name=lucy), User(id=4, name=andy), User(id=3, name=edison), User(id=2, name=lee), User(id=1, name=jack)]
        //一个很简易的消息队列 ,假设从头到尾去消费队列中的消息;被消费掉的消息将用list 在移除掉
        User consume =  (User) listOperations.rightPop(key);
        log.info(" 被消费掉的消息:" + consume);
        //被消费掉的消息:User(id=1, name=jack)
        log.info(" 此时队列中还存在的消息" + redisTemplate.opsForList().range(key, 0, -1));
        //[User(id=5, name=lucy), User(id=4, name=andy), User(id=3, name=edison), User(id=2, name=lee)]
    }

3. set(集合)
set 特点 无序,不重复
简单来一个存贮读取自然去重的例子

/**
     *  redisTemplate 操作Set  数据结构,Set 特点无序不重复
     */
    @Test
    public void setTest(){
        List<String> listName = new ArrayList<>();
        listName.add( "Jack");
        listName.add("Mike");
        listName.add("Lee");
        listName.add("Blue");
        listName.add("Edison");
        listName.add("Lee");

        String key = "redis:test:set";
        SetOperations setOperations = redisTemplate.opsForSet();
        for (String name:listName) {
            setOperations.add(key, name);
        }

        Set<String> set = setOperations.members(key);
        log.info("读取到的set:{}",set);
        //读取到的set:[Mike, Jack, Blue, Edison, Lee]
    }

4. zset(sorted set:有序集合)
举一个存贮集合排序输出的例子

/**
     *  redisTemplate 操作ZSet  数据结构,Set 特点有序不重复
     */
    @Test
    public void zSet(){
        List<User> listUser = new ArrayList<>();
        listUser.add(initUser(1, "jack"));
        listUser.add(initUser(4, "mick"));
        listUser.add(initUser(3, "xue"));
        listUser.add(initUser(2, "edison"));

        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        String key = "redis:zSet:test";
        for (User user: listUser
             ) {
            zSetOperations.add(key, user, user.getId());
        }
        long size = zSetOperations.size(key);

        Set<User> setUser = zSetOperations.range(key,0L,size);
        Set<User> setReverseUser =  zSetOperations.reverseRange(key,0L,size);
        log.info("从低到高排行榜:{}",setUser);
        //[User(id=1, name=jack), User(id=2, name=edison), User(id=3, name=xue), User(id=4, name=mick)]
        log.info("从高到低排行榜:{}",setReverseUser);
        //[User(id=4, name=mick), User(id=3, name=xue), User(id=2, name=edison), User(id=1, name=jack)]
    }

5. hash(哈希)
Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。

/**
     *  redisTemplate 操作Map  数据结构
     */
    @Test
       /**
     *  redisTemplate 操作Map  数据结构
     */
    @Test
    public void mapTest(){
        List<User> listUser = new ArrayList<>();
        listUser.add(initUser(1, "jack"));
        listUser.add(initUser(4, "mick"));
        listUser.add(initUser(3, "xue"));
        listUser.add(initUser(4, "xue"));
        listUser.add(initUser(2, "edison"));

        String key = "redis:test:map";
        HashOperations hashOperations = redisTemplate.opsForHash();
        for (User user: listUser
        ) {
            hashOperations.put(key, user.getName(), user);
        }
        // 获取全部信息
       Map<String,User> map = hashOperations.entries(key);
        log.info("获取全部信息:{}",map);
        //{jack=User(id=1, name=jack), xue=User(id=4, name=xue), mick=User(id=4, name=mick), edison=User(id=2, name=edison)}
        // 获取指定的实体信息
       User user = (User)hashOperations.get(key,"xue");
        log.info(" 获取指定的实体信息:{}",user);
        //获取指定的实体信息:User(id=4, name=xue)

    }

最后上测试类中完整的代码

package redis;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import org.springframework.data.redis.core.*;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * @description: redis 测试类
 * @author: Edison
 * @create: 2020-10-18 13:46
 **/

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = MainApplication.class)
public class RedisTest {
    private static final Logger log= LoggerFactory.getLogger(RedisTest.class);

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ObjectMapper objectMapper;

    public User initUser(int id,String name){
        User user = new User();
        user.setId(id);
        user.setName(name);

        return user;
    }

    /**
     * redisTemplate 操作简单的String 数据结构,存储简单的String 类型数据
     */
    @Test
    public  void StringTest(){
        final String value = "this is a String";
        final String key = "redis:redisTemplate:string";
        ValueOperations valueOperations = redisTemplate.opsForValue();
        // set value
        valueOperations.set(key,value);
        //get value
        Object getValue = valueOperations.get(key);
        log.info("redis 读取出的内容:{}",getValue);
    }

    /**
     * redisTemplate 操作String 数据结构,存储实体类信息
     * @throws Exception
     */
    @Test
    public void entityTest() throws Exception{
       ValueOperations valueOperations = redisTemplate.opsForValue();
       String key = "redis:test:entity";
       User user = initUser(1,"edison");
       // objectMapper 用于String 与 user 实体类映射
       String value = objectMapper.writeValueAsString(user);
        valueOperations.set(key,value);
        User v =  objectMapper.readValue(valueOperations.get(key).toString(),User.class);
        log.info("redis 读取出的内容:{}",v);
    }



    /**
     * redisTemplate 操作 list 数据结构
     */
    @Test
    public void list(){
        List<User> listUser = new ArrayList<>();
        listUser.add(initUser(1,"jack"));
        listUser.add(initUser(2,"lee"));
        listUser.add(initUser(3,"edison"));
        listUser.add(initUser(4,"andy"));
        listUser.add(initUser(5,"lucy"));
        log.info("listUser 中的的信息 {}",listUser);


        String key = "redis:list";
        ListOperations listOperations = redisTemplate.opsForList();

        // 为了每次单元测试运行的结果是一样的,在给这个list 添加元素之前先从缓存中删除这个list
        redisTemplate.delete(key);

        // list 是有序的 尝试从list的左边开始存贮元素
        listOperations.leftPushAll(key, listUser);
        List listUserFromRedis = redisTemplate.opsForList().range(key, 0, -1);
        log.info(" 从redis中读取list信息: {}",listUserFromRedis );
        //[User(id=5, name=lucy), User(id=4, name=andy), User(id=3, name=edison), User(id=2, name=lee), User(id=1, name=jack)]
        //一个很简易的消息队列 ,假设从头到尾去消费队列中的消息;被消费掉的消息将用list 在移除掉
        User consume =  (User) listOperations.rightPop(key);
        log.info(" 被消费掉的消息:" + consume);
        //被消费掉的消息:User(id=1, name=jack)
        log.info(" 此时队列中还存在的消息" + redisTemplate.opsForList().range(key, 0, -1));
        //[User(id=5, name=lucy), User(id=4, name=andy), User(id=3, name=edison), User(id=2, name=lee)]
    }

    /**
     *  redisTemplate 操作Set  数据结构,Set 特点无序不重复
     */
    @Test
    public void setTest(){
        List<String> listName = new ArrayList<>();
        listName.add( "Jack");
        listName.add("Mike");
        listName.add("Lee");
        listName.add("Blue");
        listName.add("Edison");
        listName.add("Lee");

        String key = "redis:test:set";
        SetOperations setOperations = redisTemplate.opsForSet();
        for (String name:listName) {
            setOperations.add(key, name);
        }

        Set<String> set = setOperations.members(key);
        log.info("读取到的set:{}",set);
        //读取到的set:[Mike, Jack, Blue, Edison, Lee]
    }

    /**
     *  redisTemplate 操作ZSet  数据结构,Set 特点有序不重复
     */
    @Test
    public void zSet(){
        List<User> listUser = new ArrayList<>();
        listUser.add(initUser(1, "jack"));
        listUser.add(initUser(4, "mick"));
        listUser.add(initUser(3, "xue"));
        listUser.add(initUser(2, "edison"));

        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        String key = "redis:zSet:test";
        for (User user: listUser
             ) {
            zSetOperations.add(key, user, user.getId());
        }
        long size = zSetOperations.size(key);

        Set<User> setUser = zSetOperations.range(key,0L,size);
        Set<User> setReverseUser =  zSetOperations.reverseRange(key,0L,size);
        log.info("从低到高排行榜:{}",setUser);
        log.info("从高到低排行榜:{}",setReverseUser);
    }

    /**
     *  redisTemplate 操作Map  数据结构
     */
    @Test
    public void mapTest(){
        List<User> listUser = new ArrayList<>();
        listUser.add(initUser(1, "jack"));
        listUser.add(initUser(4, "mick"));
        listUser.add(initUser(3, "xue"));
        listUser.add(initUser(4, "xue"));
        listUser.add(initUser(2, "edison"));

        String key = "redis:test:map";
        HashOperations hashOperations = redisTemplate.opsForHash();
        for (User user: listUser
        ) {
            hashOperations.put(key, user.getName(), user);
        }
        // 获取全部信息
       Map<String,User> map = hashOperations.entries(key);
        log.info("获取全部信息:{}",map);
        //{jack=User(id=1, name=jack), xue=User(id=4, name=xue), mick=User(id=4, name=mick), edison=User(id=2, name=edison)}
        // 获取指定的实体信息
       User user = (User)hashOperations.get(key,"xue");
        log.info(" 获取指定的实体信息:{}",user);
        //获取指定的实体信息:User(id=4, name=xue)

    }
}

暂时写这么多了,RedisTemplate 有很多API ,有空可以去看看源码。合理使用工具会使工作效率大幅度提升。That is all ,祝各位老板早点下班Many thanks !