目录
Redis是一个具有高性能的、基于Key-value结构化存储的缓存中间件,支持多种丰富的数据类型,包括String、列表List、集合Set、有序集合SortedSet及哈希Hash存储。本篇博文将基于Springboot整合Redis的项目以实际业务场景为例,实现上述各种数据结构,使读者真正掌握Redis在实际项目中的使用。
字符串
业务场景: 将个人信息存入缓存,实现每次前端请求获取个人详情时直接从缓存读取为例。整个过程的源代码如下:
import com.debug.middleware.server.entity.Person; 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.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; /** * @className: * @PackageName: com.debug.middleware * @author: youjp * @create: 2020-03-30 23:25 * @description: * @Version: 1.0 */ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest public class RedisTest { private static Logger logger=LoggerFactory.getLogger(RedisTest.class); @Autowired private RedisTemplate redisTemplate; //采用RedisTemplate将字符串信息写入缓存并读取出来 @Autowired private ObjectMapper objectMapper; //定义JSON序列化与反系列化框架类 @Test public void StringTest() throws Exception{ logger.info("---开始String类型---操作的实战"); Person person=new Person(1001,23,"小明"); final String key="redis:template:two:object"; final String content= objectMapper.writeValueAsString(person); //Redis通用操作组件 ValueOperations valueOperations=redisTemplate.opsForValue(); valueOperations.set(key,content); logger.info("写入缓存的内容"+content); //从缓存中读取信息 Object result=valueOperations.get(key); if(result!=null){ Person resp= objectMapper.readValue(result.toString(),Person.class); logger.info("缓存中取出来的内容"+resp); } } }
Person对象
import lombok.Data; @Data public class Person implements Serializable { //注意:这里的person对象有实现序列化 /** * 编号 */ private Integer no; /** * 年龄 */ private Integer age; /** * 姓名 */ private String name; public Person(Integer no, Integer age, String name) { this.no = no; this.age = age; this.name = name; } public Person() { } }
运行测试StringTest单元的方法,即可看到控制台的输出结果:
列表
Redis的列表类型和Java的列表类型很相似,用于存储一系列具有相同类型得数据。其底层对于数据得存储和读取可以理解为一个“数据对列”,往List中添加数据时,即相当于往对列中得某个位置插入数据(比如从队尾插入数据);而从List获取数据时,即相当于从对列中的某个位置获取数据(比如从对头获取数据)。
案例: 将一组已经排好序得用户对象列表存储在缓存中,按照排名得先后顺序获取出来并输出打印到控制台上。对于这样的需求,首先需要定义一个已经排好序得用户对象列表,然后将其存储到Redis的List中,最后按照排名先后顺序将每个用户实体获取出来。整个过程源代码如下:
import com.debug.middleware.server.entity.Person; 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.ListOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.ArrayList; import java.util.List; /** * @className: * @PackageName: com.debug.middleware * @author: youjp * @create: 2020-03-30 23:25 * @description: * @Version: 1.0 */ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest public class RedisTest { private static Logger logger=LoggerFactory.getLogger(RedisTest.class); @Autowired private RedisTemplate redisTemplate; //采用RedisTemplate将字符串信息写入缓存并读取出来 @Autowired private ObjectMapper objectMapper; //定义JSON序列化与反系列化框架类 //列表类型 @Test public void two(){ List<Person> list=new ArrayList<>(); list.add(new Person(1,22,"xiaolan1")); list.add(new Person(2,23,"xiaolan2")); list.add(new Person(3,24,"xiaolan3")); logger.info("---构造已经排序好的用户对象列表:{}",list); final String key="redis:template:test:2"; //Redis通用操作组件 ListOperations<String,Person> listOperations=redisTemplate.opsForList(); for (Person person:list){ //往队列中添加数据从队尾中添加 listOperations.leftPush(key,person); } logger.info("---获取Redis中List的数据从对头中遍历获取,直到没有元素为止---"); //获取Redis中List的数据从对头中遍历获取,直到没有元素为止 Person per= listOperations.rightPop(key); while (per!=null){ logger.info("当前数据:{}",per); per=listOperations.rightPop(key); } } }
查看控制台打印结果:
Set集合
Redis的set集合类型和高等数学中集合很类似,用于存储具有相同类型或特性得不重复数据,即Redis中集合Set存储得数据是唯一的,其底层的数据结构是通过哈希表来实现的,所以它在添加、删除、查找操作的复杂度均为O(1);
案例:给定一组用户姓名列表,要求剔除具有相同姓名得人员并组成新的集合,存放入缓存中并用于前端读取访问。代码如下:
import com.debug.middleware.server.entity.Person; 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.ListOperations; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.SetOperations; import org.springframework.data.redis.core.ValueOperations; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import java.util.ArrayList; import java.util.List; /** * @className: * @PackageName: com.debug.middleware * @author: youjp * @create: 2020-03-30 23:25 * @description: * @Version: 1.0 */ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest public class RedisTest { private static Logger logger=LoggerFactory.getLogger(RedisTest.class); @Autowired private RedisTemplate redisTemplate; //采用RedisTemplate将字符串信息写入缓存并读取出来 @Autowired private ObjectMapper objectMapper; //定义JSON序列化与反系列化框架类 //集合案例 @Test public void three(){ List<String> userList=new ArrayList<>(); userList.add("debug"); userList.add("jack"); userList.add("修罗"); userList.add("大圣"); userList.add("修罗"); userList.add("debug"); userList.add("heart"); logger.info("待处理得用户姓名列表:{}",userList); final String key="redis:template:test:3"; SetOperations setOperations=redisTemplate.opsForSet(); //遍历访问,剔除相同的用户并塞入集合中,最终存入缓存 for(String name:userList){ setOperations.add(key,name); } //从缓存中取出用户对象集合 Object result= setOperations.pop(key); while (result!=null){ logger.info("从缓存对象中获取得用户集合-当前用户:{}",result); result= setOperations.pop(key); } } }
运行此单元测试方法,查看控制台
有序集合
Redis 的有序集合SortedSet和集合Set具有某些相同的特性,即存储的数据是不重复的、无序的、唯一的;而这两者的不同之处在于SortedSet可以通过底层的Score(分数/权重)对数据进行排序,实现存储的集合数据既不重复又有序,可以说其包含了列表List、集合Set的特性。
案例:给一组无序的对象数据列表,将其存入缓存。要求按照分数从小到大顺序进行排序,最终从缓存中获取出来。
import com.debug.middleware.server.entity.Person; 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.Set; /** * @className: * @PackageName: com.debug.middleware * @author: youjp * @create: 2020-03-30 23:25 * @description: * @Version: 1.0 */ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest public class RedisTest { private static Logger logger = LoggerFactory.getLogger(RedisTest.class); @Autowired private RedisTemplate redisTemplate; //采用RedisTemplate将字符串信息写入缓存并读取出来 @Autowired private ObjectMapper objectMapper; //定义JSON序列化与反系列化框架类 @Test public void four() { List<Person> list = new ArrayList<>(); list.add(new Person(1001, 23, "zhangsan1", 120.0)); list.add(new Person(1002, 28, "zhangsan2", 110.0)); list.add(new Person(1003, 22, "zhangsan3", 140.0)); list.add(new Person(1004, 24, "zhangsan4", 140.0)); list.add(new Person(1005, 20, "zhangsan5", 90.0)); list.add(new Person(1006, 18, "zhangsan6", 150.0)); logger.info("待排序得的数据{}", list); final String key="redis:template:test:4"; //获取zset操作对象 ZSetOperations zSetOperations=redisTemplate.opsForZSet(); for (Person per:list){ //将元素添加到有序集合Sorted中 zSetOperations.add(key,per,per.getScore()); } //前端获取分数排名靠前的用户列表 Long size= zSetOperations.size(key); //从小到大排序 Set<Person> set= zSetOperations.range(key,0,size); //从大到小 // Set<Person> reseveSet= zSetOperations.reverseRange(key,0,size); for(Person person:set){ logger.info("从缓存中获取的用户列表,当前记录{}",person); } } }
其中Person对象:
import lombok.Data; import java.io.Serializable; /** * @className: * @PackageName: com.debug.middleware.server.entity * @author: youjp * @create: 2020-03-31 21:35 * @description: TODO 用户对象 * @Version: 1.0 */ @Data public class Person implements Serializable { /** * 编号 */ private Integer no; /** * 年龄 */ private Integer age; /** * 姓名 */ private String name; /** * 分数 */ private double score; public Person(Integer no, Integer age, String name, double score) { this.no = no; this.age = age; this.name = name; this.score = score; } public Person(Integer no, Integer age, String name) { this.no = no; this.age = age; this.name = name; } public Person() { } }
运行测试方法,查看控制台:
可以看到最终按照分数由小到大排列。当分数相同时,可以去Person对象重写equals和hashCode方法自定义比较规则。
在实际生产环境中,Redis的有序集合Sorted常用于充值排行榜、积分排行榜等应用场景。
Hash哈希存储
Redis的哈希存储和java的HashMap数据结构有点类似,其底层数据结构是由Key-Value组成的映射表,而其Value又是由Filed-Value对组成,特别适用于具有映射数据对象的存储。
实例: 将学生对象和水果对象进行存储。
import com.debug.middleware.server.entity.Fruit; import com.debug.middleware.server.entity.Person; import com.debug.middleware.server.entity.Student; 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; /** * @className: * @PackageName: com.debug.middleware * @author: youjp * @create: 2020-03-30 23:25 * @description: * @Version: 1.0 */ @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest public class RedisTest { private static Logger logger = LoggerFactory.getLogger(RedisTest.class); @Autowired private RedisTemplate redisTemplate; //采用RedisTemplate将字符串信息写入缓存并读取出来 @Autowired private ObjectMapper objectMapper; //定义JSON序列化与反系列化框架类 //hash案例 @Test public void five(){ List<Fruit> fruitList=new ArrayList<>(); List<Student> studentList=new ArrayList<>(); studentList.add(new Student("01","debug","大圣")); studentList.add(new Student("02","jack","修罗")); studentList.add(new Student("03","sam","上古")); studentList.add(new Student("04","jhon","女娲")); logger.info("待存储得的数据{}", studentList); fruitList.add(new Fruit("apple","红色")); fruitList.add(new Fruit("orange","橙色")); fruitList.add(new Fruit("banana","黄色")); logger.info("待存储得的数据{}", fruitList); final String fruitKey="redis:template:test:fruit"; final String studentKey="redis:template:test:student"; HashOperations hashOperations= redisTemplate.opsForHash(); for(Student student:studentList){ //将元素添加到hash缓存 hashOperations.put(studentKey,student.getNo(),student); } for(Fruit fruit:fruitList){ //将元素添加到hash缓存 hashOperations.put(fruitKey,fruit.getName(),fruit); } //获取学生对象列表和水果对象列表 Map<String,Student> smap= hashOperations.entries(studentKey); logger.info("获取学生列表:{}",smap); Map<String,Fruit> fmap= hashOperations.entries(fruitKey); logger.info("获取水果列表:{}",fmap); //获取指定的学生对象 String no="02"; Student s= (Student) hashOperations.get(studentKey,no); logger.info("获取指定的学生对象:{} ->{}",no,s); //获取指定的水果 String fruitName="apple"; Fruit fruit= (Fruit) hashOperations.get(fruitKey,fruitName); logger.info("获取指定的水果:{}->{}",fruitName,fruit); //删除apple的水果 hashOperations.delete(fruitKey,fruitName); boolean ifExits= hashOperations.hasKey(fruitKey,fruitName); logger.info("水果为{},是否还存在:{}",fruitName,ifExits); } }
其中,学生的实体类为:
import lombok.Data; import lombok.ToString; import java.io.Serializable; /** * @ClassName: * @PackageName: com.debug.middleware.server.entity * @author: youjp * @create: 2020-04-02 08:41 * @description: * @Version: 1.0 */ @ToString @Data public class Student implements Serializable { /** * 学号 */ private String no; /** * 账户 */ private String username; /** * 姓名 */ private String name; public Student(String no, String username, String name) { this.no = no; this.username = username; this.name = name; } public Student() { } }
水果的实体类为:
import lombok.Data; import lombok.ToString; import java.io.Serializable; /** * @ClassName: * @PackageName: com.debug.middleware.server.entity * @author: youjp * @create: 2020-04-02 08:40 * @description: TODO 水果类 * @Version: 1.0 */ @ToString @Data public class Fruit implements Serializable { /** * 名称 */ private String name; /** * 颜色 */ private String color; public Fruit(String name, String color) { this.name = name; this.color = color; } public Fruit() { } }
运行此测试,查看控制台结果
源码下载:
https://gitee.com/yjp245/middleware_study.git