redis五种数据结构详解(java实现对应的案例)

一、简述

Redis是一款高性能的键值对存储数据库,它支持五种基本数据类型,分别是字符串(String)、列表(List)、哈希(Hash)、集合(Set)、有序集合(Sorted Set)。

二、五种基本数据类型

2.1 字符串(String)

String是Redis最基本的类型,一个key对应一个value,它是数据安全的,并且可以包含任何数据,一个Redis的字符串最多可以是512M(字符串长度小于1M的时候,每次扩容都是加倍现有空间,如果超过1M,扩容的时候只会扩容1M空间)

常用的应用场景:缓存用户信息、用户登录状态、配置文件等等

常用命令

复制代码
set <key><value> 添加键值对
get <key> 查询对应键值
append <key><value>将给定的 追加到原值的末尾
strlen <key> 获得值的长度
setnx <key><value> 只有在 key 不存在时 设置 key 的值
incr <key> 将 key 中储存的数字值增1只能对数字值操作,如果为空,新增值为
decr <key> 将 key 中储存的数字值减1只能对数字值操作,如果为空,新增值为-1
mset <key1><value1><key2><value2> ..... 同时设置一个或多个 key-value对
mget <key1><key2><key3> .....同时获取一个或多个 value
msetnx <key1><value1><key2><value2> ..... 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。
getrange <key><起始位置><结束位置> 获得值的范围,类似java中的substring,前包,后包
setrange <key><起始位置><value>用 <value> 覆写<key> 所储存的字符串值,从<起始位置>开始(索引从0开始)。
setex <key><过期时间><value> 设置键值的同时,设置过期时间,单位秒。

2.2 列表(List)

列表相当于是单个键对应多个值,它是一个有序的字符串元素集合,它按照插入的顺序来存储元素,可以在列表的头部或尾部进行插入和删除操作。它的底层实际是一个双向列表,对两端的操作性能很高,通过索引下标的操作中间的节点性能会较差

常用的应用场景:消息队列,任务队列,历史记录等

常用命令

复制代码
lpush/rpush <key><value1><value2><value3> .... 从左边/右边插入一个或多个值。
lpop/rpop <key> 从左边/右边吐出一个值。值在键在,值光键亡。
rpoplpush <key1><key2>从<key1> 列表右边吐出一个值,插到<key2>列表左边。
lrange <key><start><stop> 按照索引下标获得元素(从左到右)
lrange key 0 -1 0左边第一个,-1右边第一个,(0-1表示获取所有)
lindex <key><index> 按照索引下标获得元素(从左到右)
llen <key> 获得列表长度
linsert <key> before <value><newvalue> 在<value>的后面插入<newvalue>插入值
lrem <key><n><value> 从左边删除n个value(从左到右)
lset<key><index><value> 将列表key下标为index的值替换成value

2.3 集合(Set)

无序的字符串元素集合,跟List有点相似,不一样的是集合中的元素是唯一的,不允许重复的

常用的应用场景:签到系统,好友关系,去重等功能

常用的命令

复制代码
sadd <key><value1><value2> ..... 将一个或多个 member 元素加入到集合 key 中,已经存在的member 元素将被忽略
smembers <key>取出该集合的所有值。
sismember <key><value>判断集合<key>是否为含有该<value>值,有1,没有0
scard<key>返回该集合的元素个数。
srem <key><value1><value2> .... 删除集合中的某个元素。
spop <key>随机从该集合中吐出一个值。
srandmember <key><n>随机从该集合中取出n个值。不会从集合中删除 。
smove <source><destination>value把集合中一个值从一个集合移动到另一个集合
sinter <key1><key2>返回两个集合的交集元素。
sunion <key1><key2>返回两个集合的并集元素。
sdiff <key1><key2>返回两个集合的差集元素(key1中的,不包含key2中的)

2.4 哈希(Hash)

哈希是一个键值对的集合,其中键和值都是字符串,它可以用来存储对象的属性,类似与关系型数据库中的表记录

常用的应用场景:存储用户信息,商品信息等对象数据

常用的命令

复制代码
hset <key><field><value>给<key>集合中的 <field>键赋值<value>
hget <key1><field>从<key1>集合<field>取出 value
hmset <key1><field1><value1><field2><value2>... 批量设置hash的值
hexists<key1><field>查看哈希表 key 中,给定域 field 是否存在。
hkeys <key>列出该hash集合的所有field
hvals <key>列出该hash集合的所有value
hincrby <key><field><increment>为哈希表 key 中的域 field 的值加上增量 1 -1
hsetnx <key><field><value>将哈希表 key 中的域 field 的值设置为 value ,当且仅当域 field 不存在

2.5 有序集合(Sorted Set/zset)

sorted Set和zset是一个东西,他们都是同一数据类型,只是称呼不同。有序集合和set是非常的相似,不同之处是有序集合的每个成员都关联了一个评分(score),这个评分(score)被用来按照从最低分到最高分的方式排序集合中的成员。集合成员是唯一的,但是评分可以重复。因为元素是有序的,所以我们也可以跟快的根据评分(score)或者次序(position)来获取范围元素

常用的应用场景:排行榜,热门话题等功能

常用的命令

复制代码
zadd <key><score1><value1><score2><value2>...将一个或多个 member 元素及其score 值加入到有序集 key 当中
zrange <key><start><stop> [WITHSCORES] 返回有序集 key 中,下标在<start><stop>之间的元素,带WITHSCORES,可以让分数一起和值返回到结果集。
zrangebyscore key minmax [withscores] [limit offset count]返回有序集 key 中,所有 score 值介于 min 和 max 之间(包括等于 min 或 max )的成员。有序集成员按 score 值递增(从小到大)次序排列。
zrevrangebyscore key maxmin [withscores] [limit offset count] 同上,改为从大到小排列。
zincrby <key><increment><value> 为元素的score加上增量
zrem <key><value>删除该集合下,指定值的元素
zcount <key><min><max>统计该集合,分数区间内的元素个数
zrank <key><value>返回该值在集合中的排名,从0开始。

三、五种数据类型使用案例

3.1 连接redis

XML 复制代码
  <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-redis</artifactId>
    <version>1.4.7.RELEASE</version>
  </dependency>

XML 复制代码
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.8.0</version>
</dependency>

注意点:这个是springboot的jar,其实也可以直接导入jedis的ajr

下面连接redis调用的都是这个RedisTestUtil工具类

java 复制代码
package com.study.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * @author Administrator
 * @version 1.0
 * @project study
 * @description
 * @date 2025/5/20 星期二 17:11:46
 */
public class RedisTestUtil {
    /**
     * 地址
     */
    private static final String HOST = "127.0.0.1";
    /**
     * 端口
     */
    private static final int PORT = 6379;
    /**
     * 缓存时间
     */
    private static final int TIMEOUT = 30000;
    /**
     * 密码
     */
    private static final String PASSWORD = "hm23";
    /**
     * 使用的redis库
     */
    private static final int DATABASE = 0;

    private static final JedisPool jedisPool;

    static {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(100);
        config.setMaxIdle(10);
        config.setMinIdle(5);
        config.setTestOnBorrow(true);

        jedisPool = new JedisPool(config, HOST, PORT, TIMEOUT, PASSWORD, DATABASE);
    }

    public static Jedis getJedis() {
        return jedisPool.getResource();
    }

    public static void close(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }
}

注意点:如果想springboot连接redis可以查看springboot连接redis_springboot链接单机redis bean注入地址信息-CSDN博客

3.2 String使用案例(存储token)

用字符串缓存一个token

java 复制代码
 public static void main(String[] args) {
        String stringKey = "auth:token:";
        String token = "token222222222222222222222222222222";

        //连接redis
        Jedis jedis = RedisTestUtil.getJedis();
        // redis缓存token
        jedis.set(stringKey, token);

        //redis获取对应的值
        String content = jedis.get(stringKey);
        System.out.println("获取到的值为>>>>>>>>"+content);

    }

3.3 List使用案例(用户操作历史记录)

用list缓存历史记录,每个用户对应一个list

java 复制代码
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;

import java.time.LocalDateTime;
import java.util.Date;

/**
 * @author Administrator
 * @version 1.0
 * @project study
 * @description
 * @date 2025/5/20 星期二 17:29:29
 */
@Data
@Accessors(chain = true)
@AllArgsConstructor
@NoArgsConstructor
public class HistoryRecord {
    private Integer id;
    private String userId;      // 用户ID
    private String content;     // 记录内容
    private String time; // 记录时间
}
java 复制代码
public class RedisTest {
    private static final String HISTORY_KEY_PREFIX = "user:history:";
    private static final int MAX_RECORDS = 20; // 每个用户最大记录数

    public static void main(String[] args) {
        // 缓存userId为1的历史记录
        RedisTest test = new RedisTest();
        for (int i = 0; i < 10; i++) {
            test.addRecord(i,"1","点击了"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        //缓存userId为2的历史记录
        for (int i = 0; i < 10; i++) {
            test.addRecord(i,"2","点击了"+i);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        // 获取userId为1历史记录
        System.out.println("==============================获取userId为1历史记录=============================================");
        List<HistoryRecord> historyRecordList = test.getRecords("1");
        for (HistoryRecord historyRecord : historyRecordList) {
            System.out.println(JSON.toJSONString(historyRecord));
        }
        // 获取用户最新的n条历史记录
        System.out.println("==============================获取用户最新的n条历史记录=============================================");
        List<HistoryRecord> historyRecordList2 = test.getLatestRecords("1",2);
        for (HistoryRecord historyRecord : historyRecordList2) {
            System.out.println(JSON.toJSONString(historyRecord));
        }
        System.out.println("==============================删除单条历史记录=============================================");
        test.deleteRecord("1","2");// 第二个参数为操作id
        System.out.println("==============================清空用户的历史记录=============================================");
        test.clearHistory("1");

    }

    /**
     * 添加历史记录(自动去重,保持最新记录)
     */
    public  void addRecord(Integer id,String userId, String content) {
        Jedis jedis = RedisTestUtil.getJedis();

        String key = HISTORY_KEY_PREFIX + userId;
        HistoryRecord record = new HistoryRecord(id,userId,content, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        String json = JSON.toJSONString(record);

        // 1. 从左边删除,先删除旧记录(如果存在)
        jedis.lrem(key, 0, json);

        // 2. 添加新记录到列表头部(最新记录在前)
        jedis.lpush(key, json);

        // 3. 限制列表长度,超出部分自动删除
        if (jedis.llen(key) > MAX_RECORDS) {
            jedis.rpop(key);
        }
    }

    /**
     * 获取用户的历史记录(按时间倒序)
     */
    public List<HistoryRecord> getRecords(String userId) {
        Jedis jedis = RedisTestUtil.getJedis();
        String key = HISTORY_KEY_PREFIX + userId;
        List<String> jsonList = jedis.lrange(key, 0, -1);
        List<HistoryRecord> records = new ArrayList<>();

        for (String json : jsonList) {
            records.add(JSON.parseObject(json, HistoryRecord.class));
        }
        return records;

    }

    /**
     * 获取用户最新的N条历史记录
     */
    public List<HistoryRecord> getLatestRecords(String userId, int count) {

        Jedis jedis = RedisTestUtil.getJedis();
        String key = HISTORY_KEY_PREFIX + userId;
        List<String> jsonList = jedis.lrange(key, 0, count - 1);
        List<HistoryRecord> records = new ArrayList<>();

        for (String json : jsonList) {
            records.add(JSON.parseObject(json, HistoryRecord.class));
        }
        return records;

    }

    /**
     * 删除单条记录实录
     * @param userId
     * @param recordId 操作的id
     */
    public void deleteRecord(String userId, String recordId) {
        Jedis jedis = RedisTestUtil.getJedis();
        String key = HISTORY_KEY_PREFIX + userId;
        List<String> jsonList = jedis.lrange(key, 0, -1);

        // 查找并删除匹配ID的记录
        for (String json : jsonList) {
            HistoryRecord record = JSON.parseObject(json, HistoryRecord.class);
            if (record.getId().equals(Integer.valueOf(recordId))) {
                jedis.lrem(key, 0, json);
                break;
            }
        }

    }

    /**
     * 清空用户的历史记录
     */
    public void clearHistory(String userId) {
        Jedis jedis = RedisTestUtil.getJedis();
        String key = HISTORY_KEY_PREFIX + userId;
        jedis.del(key);
    }
}

3.3 Set使用案例(当月签到情况)

利用set缓存一个签到功能,通过用户id可以查看到当月签到情况

java 复制代码
public class RedisTest {

    private static final String HISTORY_KEY_PREFIX = "user:history:";
    private static final int MAX_RECORDS = 20; // 每个用户最大记录数

    private static final String SIGN_KEY_PREFIX = "sign:";

    public static void main(String[] args) {

        Long userId = 1001L;
        RedisTest test = new RedisTest();
        // 签到
        boolean isNewSign = test.doSign(userId);
        System.out.println("签到结果: " + (isNewSign ? "新签到" : "已签到"));

        // 检查是否签到
        System.out.println("今日是否签到: " + test.checkSign(userId));

        // 获取当月签到次数
        System.out.println("当月签到次数: " + test.getSignCount(userId));

        // 获取连续签到次数
        System.out.println("连续签到次数: " + test.getContinuousSignCount(userId));

        // 获取签到详情
        List<Boolean> signDetail = test.getSignDetail(userId);
        System.out.println("当月签到详情: " + signDetail);

    }

    // 生成 Key(格式:sign:{userId}:{yearmonth})
    private String getSignKey(Long userId) {
        LocalDate now = LocalDate.now();
        String yearMonth = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
        return SIGN_KEY_PREFIX + userId + ":" + yearMonth;
    }



    // 签到(返回 true 表示首次签到,false 表示已签到)
    public boolean doSign(Long userId) {
        Jedis jedis = RedisTestUtil.getJedis();
        LocalDate now = LocalDate.now();
        int dayOfMonth = now.getDayOfMonth();
        String key = getSignKey(userId);

        // 使用 SADD 命令添加成员(Set 自动去重,存在则返回 0)
        return jedis.sadd(key, String.valueOf(dayOfMonth)) == 1;
    }

    // 检查今日是否已签到
    public boolean checkSign(Long userId) {
        Jedis jedis = RedisTestUtil.getJedis();
        LocalDate now = LocalDate.now();
        int dayOfMonth = now.getDayOfMonth();
        String key = getSignKey(userId);

        // 使用 SISMEMBER 命令检查成员是否存在
        return jedis.sismember(key, String.valueOf(dayOfMonth));
    }

    // 获取当月签到总次数
    public long getSignCount(Long userId) {
        Jedis jedis = RedisTestUtil.getJedis();
        String key = getSignKey(userId);
        // 使用 SCARD 命令获取集合大小
        return jedis.scard(key);
    }

    // 获取当月连续签到次数
    public long getContinuousSignCount(Long userId) {
        Jedis jedis = RedisTestUtil.

3.4 Hash使用案例(缓存商品信息)

利用Hash缓存商品信息

java 复制代码
@Data
@Accessors(chain = true)
public class Product {

    private Long productId;
    private String name;
    private Double price;
    private Integer stock;
    private String category;
    private String description;
    private Long updateTime;

}
java 复制代码
public class RedisTest {



    private final Jedis jedis = RedisTestUtil.getJedis();

    private static final String PRODUCT_KEY_PREFIX = "product:";


    public static void main(String[] args) {
    // 创建商品对象
        Product product = new Product();
        product.setProductId(1001L);
        product.setName("iPhone 15 Pro");
        product.setPrice(6999.00);
        product.setStock(100);
        product.setCategory("手机/数码");
        product.setDescription("A17芯片,灵动岛设计");
        product.setUpdateTime(System.currentTimeMillis() / 1000);
        RedisTest redisTest = new RedisTest();

        // 存储商品到 Redis(过期时间 1天 = 86400秒)
        redisTest.saveProduct(product, 86400);
        System.out.println("商品已缓存");

        // 获取所有属性
        Map<String, String> productData = redisTest.getProduct(1001L);
        System.out.println("商品信息:" + productData);

        // 更新库存
        redisTest.updateProductField(1001L, "stock", "80");
        System.out.println("库存更新后:" + redisTest.getProductField(1001L, "stock"));

        // 删除描述字段
        redisTest.deleteProductField(1001L, "description");
        System.out.println("删除描述后:" + productData.get("description")); // 应输出 null

    }
    // 存储商品信息到 Redis Hash(带过期时间)
    public void saveProduct(Product product, int expireSeconds) {
        String key = getProductKey(product.getProductId());
        Map<String, String> hash = new HashMap<>();
        hash.put("name", product.getName());
        hash.put("price", String.valueOf(product.getPrice()));
        hash.put("stock", String.valueOf(product.getStock()));
        hash.put("category", product.getCategory());
        hash.put("description", product.getDescription());
        hash.put("update_time", String.valueOf(product.getUpdateTime()));

        jedis.hmset(key, hash); // 批量设置字段
        jedis.expire(key, expireSeconds); // 设置过期时间(秒)
    }

    // 获取商品的所有属性
    public Map<String, String> getProduct(Long productId) {
        String key = getProductKey(productId);
        return jedis.hgetAll(key); // 返回所有字段和值
    }

    // 获取单个属性(如价格)
    public String getProductField(Long productId, String field) {
        String key = getProductKey(productId);
        return jedis.hget(key, field);
    }

    // 更新商品属性(如库存)
    public Long updateProductField(Long productId, String field, String value) {
        String key = getProductKey(productId);
        return jedis.hset(key, field, value); // 返回 1 表示新增字段,0 表示更新现有字段
    }

    // 删除商品属性(如描述)
    public Long deleteProductField(Long productId, String field) {
        String key = getProductKey(productId);
        return jedis.hdel(key, field); // 返回删除的字段数
    }

    // 生成完整 Key
    private String getProductKey(Long productId) {
        return PRODUCT_KEY_PREFIX + productId;
    }


}

3.4 Sort Set使用案例(排行榜)

利用Sort Set缓存一个排行榜

java 复制代码
@Accessors(chain = true)
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RankItem {

    private  int rank;      // 排名
    private  String userId; // 用户 ID
    private  double score;  // 分数
}
java 复制代码
public class RedisTest {
    private Jedis jedis = RedisTestUtil.getJedis();
    private static final String RANK_KEY = "rank:score"; // 排行榜 Key

    public static void main(String[] args) {
        RedisTest test = new RedisTest();
        // 添加测试数据
        test.updateScore("user:1001", 95.0);
        test.updateScore("user:1002", 88.0);
        test.updateScore("user:1003", 92.0);
        test.updateScore("user:1004", 76.0);
        test.updateScore("user:1005", 95.0); // 与 1001 分数相同

        // 获取前三名
        List<RankItem> top3 = test.getTopN(3);
        System.out.println("排名前三用户为:");
        top3.forEach(System.out::println);

        // 获取用户 1002 的排名
        long rank = test.getUserRank("user:1002");
        System.out.println("用户1002排名: " + rank);
    }

    // 添加/更新用户分数
    public void updateScore(String userId, double score) {

        jedis.zadd(RANK_KEY, score, userId);

    }

    // 获取排行榜前 N 名(降序)
    public List<RankItem> getTopN(int n) {

        // 获取前 N 名(分数从高到低)
        Set<Tuple> topN = jedis.zrevrangeWithScores(RANK_KEY, 0, n - 1);

        List<RankItem> result = new ArrayList<>();
        int rank = 1;

        for (Tuple tuple : topN) {
            result.add(new RankItem(
                    rank++,
                    tuple.getElement(),  // 用户 ID
                    tuple.getScore()     // 分数
            ));
        }

        return result;

    }

    // 获取用户排名(从 1 开始)
    public long getUserRank(String userId) {

        // ZREVRANK 返回 0 为第一名
        Long rank = jedis.zrevrank(RANK_KEY, userId);
        return rank != null ? rank + 1 : -1; // -1 表示未找到

    }

    // 获取用户分数
    public double getUserScore(String userId) {

        Double score = jedis.zscore(RANK_KEY, userId);
        return score != null ? score : 0;

    }


}

参考文档:Redis五大基本数据类型(String、LIst、Set、Hash、ZSet)及其底层结构-腾讯云开发者社区-腾讯云

相关推荐
你不是我我1 小时前
【Java开发日记】基于 Spring Cloud 的微服务架构分析
java·开发语言
写bug写bug1 小时前
彻底搞懂 RSocket 协议
java·后端
比特森林探险记1 小时前
深入解析Go语言数据类型:从底层到高级应用
java·前端·golang
橘子青衫1 小时前
深入理解Callable与Future:实现Java多线程中的异步任务处理
java·后端
Magnum Lehar1 小时前
vulkan游戏引擎game_types.h和生成build.bat实现
java·算法·游戏引擎
会敲键盘的猕猴桃很大胆2 小时前
Redis实战-基于redis和lua脚本实现分布式锁以及Redission源码解析【万字长文】
java·redis·分布式·spring·lua
设计师小聂!2 小时前
JDBC+HTML+AJAX实现登陆和单表的CRUD
java·ajax·servlet·html·maven
Mr__Miss2 小时前
微服务中引入公共拦截器
java·微服务·架构
@我漫长的孤独流浪2 小时前
数据结构测试模拟题(2)
数据结构·c++·算法
黑牛先生2 小时前
【数据结构】图的存储(邻接矩阵与邻接表)
数据结构