Redis常见数据结构实战

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: Redis常见数据结构实战

目录

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单元的方法,即可看到控制台的输出结果:

20200401134307494.png

列表

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);
        }
    }
}

查看控制台打印结果:

20200401134307494.png

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);
        }
    }
}

运行此单元测试方法,查看控制台

20200401134307494.png

有序集合

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() {
    }
}

运行测试方法,查看控制台:

20200401134307494.png

可以看到最终按照分数由小到大排列。当分数相同时,可以去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() {
    }
}

运行此测试,查看控制台结果

20200401134307494.png

源码下载:

https://gitee.com/yjp245/middleware_study.git




相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
19天前
|
NoSQL 安全 测试技术
Redis游戏积分排行榜项目中通义灵码的应用实战
Redis游戏积分排行榜项目中通义灵码的应用实战
39 4
|
9天前
|
存储 消息中间件 缓存
Redis 5 种基础数据结构?
Redis的五种基础数据结构——字符串、哈希、列表、集合和有序集合——提供了丰富的功能来满足各种应用需求。理解并灵活运用这些数据结构,可以极大地提高应用程序的性能和可扩展性。
19 2
|
21天前
|
缓存 NoSQL PHP
Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出
本文深入探讨了Redis作为PHP缓存解决方案的优势、实现方式及注意事项。Redis凭借其高性能、丰富的数据结构、数据持久化和分布式支持等特点,在提升应用响应速度和处理能力方面表现突出。文章还介绍了Redis在页面缓存、数据缓存和会话缓存等应用场景中的使用,并强调了缓存数据一致性、过期时间设置、容量控制和安全问题的重要性。
36 5
|
1月前
|
存储 消息中间件 NoSQL
Redis数据结构:List类型全面解析
Redis数据结构——List类型全面解析:存储多个有序的字符串,列表中每个字符串成为元素 Eelement,最多可以存储 2^32-1 个元素。可对列表两端插入(push)和弹出(pop)、获取指定范围的元素列表等,常见命令。 底层数据结构:3.2版本之前,底层采用**压缩链表ZipList**和**双向链表LinkedList**;3.2版本之后,底层数据结构为**快速链表QuickList** 列表是一种比较灵活的数据结构,可以充当栈、队列、阻塞队列,在实际开发中有很多应用场景。
|
2月前
|
存储 消息中间件 NoSQL
Redis 数据结构与对象
【10月更文挑战第15天】在实际应用中,需要根据具体的业务需求和数据特点来选择合适的数据结构,并合理地设计数据模型,以充分发挥 Redis 的优势。
58 8
|
2月前
|
NoSQL 关系型数据库 MySQL
MySQL与Redis协同作战:优化百万数据查询的实战经验
【10月更文挑战第13天】 在处理大规模数据集时,传统的关系型数据库如MySQL可能会遇到性能瓶颈。为了提升数据处理的效率,我们可以结合使用MySQL和Redis,利用两者的优势来优化数据查询。本文将分享一次实战经验,探讨如何通过MySQL与Redis的协同工作来优化百万级数据统计。
70 5
|
2月前
|
存储 NoSQL Java
介绍下Redis 的基础数据结构
本文介绍了Redis的基础数据结构,包括动态字符串(SDS)、链表和字典。SDS是Redis自实现的动态字符串,避免了C语言字符串的不足;链表实现了双向链表,提供了高效的操作;字典则类似于Java的HashMap,采用数组加链表的方式存储数据,并支持渐进式rehash,确保高并发下的性能。
介绍下Redis 的基础数据结构
|
1月前
|
存储 NoSQL 关系型数据库
Redis的ZSet底层数据结构,ZSet类型全面解析
Redis的ZSet底层数据结构,ZSet类型全面解析;应用场景、底层结构、常用命令;压缩列表ZipList、跳表SkipList;B+树与跳表对比,MySQL为什么使用B+树;ZSet为什么用跳表,而不是B+树、红黑树、二叉树
|
1月前
|
存储 NoSQL Redis
Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList
String类型底层数据结构,List类型全面解析,ZSet底层数据结构;简单动态字符串SDS、压缩列表ZipList、哈希表、跳表SkipList、整数数组IntSet
|
存储 缓存 NoSQL
Redis实战之入门进阶到精通
Redis 是一个远程内存数据库,它不仅性能强劲,而且还具有复制特性以及为解决问题而生的独一无二的数据模型。Redis 提供了 5 种不同类型的数据结构,各式各样的问题都可以很自然地映射到这些数据结构上:Redis 的数据结构致力于帮助用户解决问题,而不会像其他数据库那样,要求用户扭曲问题来适应数据库。除此之外,通过复制、持久化(persistence)和客户端分片(client-side sharding)等特性,用户可以很方便地将 Redis 扩展成一个能够包含数百 GB 数据、每秒处理上百万次请求的系统。
Redis实战之入门进阶到精通