Redis 五大基础数据类型详解:底层原理、常用命令与 Spring Boot 实战

一、前言

Redis 是一款高性能的开源键值(Key-Value)型内存数据库,凭借读写速度快、支持高并发、数据类型丰富、功能强大等优势,被广泛应用于缓存、分布式锁、限流、消息队列、排行榜、计数器等各类业务场景。

与传统关系型数据库不同,Redis 并非只支持简单的字符串键值对,它内置了多种基础与扩展数据类型,能够适配多样化的业务存储需求。其中 String、Hash、List、Set、ZSet 是 Redis 最核心的五种基础数据类型,也是日常开发中使用频率最高的数据结构。

想要用好 Redis,不仅要掌握各类数据类型的使用命令,更要理解其底层存储结构、设计思想、特性与适用场景。本文将全面讲解 Redis 五大基础数据类型,从核心特性、底层实现、原生命令,再到 Spring Boot + Spring Data Redis 代码实战,由浅入深进行剖析,帮助大家彻底掌握 Redis 基础核心知识。

二、Redis String 字符串类型

String 是 Redis 中最基础、使用最广泛的数据类型,也是所有 Key 默认的存储类型。Redis 中所有的键(Key)本质上都是 String 类型,Value 同样基于字符串实现,可灵活存储文本、数字、二进制数据等内容。

2.1 核心特性

  1. 二进制安全 String 不会对存储内容做任何解析、转义、截断处理,能够完整保存普通字符串、数字、JSON 字符串、图片字节流、序列化对象、音频视频二进制数据等,不存在 C 语言字符串 \0 截断的问题。
  2. 容量限制 单个 String 类型的 Value 最大可存储 512MB 数据,足以满足绝大多数业务场景。
  3. 支持数字原子操作 当字符串内容为整型数字时,Redis 提供自增、自减等命令,所有数字操作均为单命令原子性,在高并发场景下无需额外加锁,天然保证线程安全。

2.2 底层实现:SDS 简单动态字符串

Redis 的 String 类型底层并不是直接使用 C 语言原生字符串,而是自研了 SDS(Simple Dynamic String,简单动态字符串),这也是 Redis 高性能、二进制安全的核心原因。

2.2.1 SDS 结构体分类

Redis 定义了五种 SDS 结构体:sdshdr5sdshdr8sdshdr16sdshdr32sdshdr64。除 sdshdr5 为极简结构外,其余四种结构设计一致,仅 lenalloc 字段的位宽不同(8/16/32/64 位),Redis 会根据字符串长度自动选择最小结构体,最大化节省内存。

  1. 标准 SDS(sdshdr8/16/32/64)内存布局
  • len:实际数据字节长度,不包含末尾 \0,读取长度时间复杂度为 O (1);
  • alloc:已分配的内存总大小,记录 buf 数组可用空间,用于内存预分配优化;
  • flags:占用 1 字节,低 3 位标识 SDS 类型,高 5 位预留未使用;
  • buf[]:字符数组,存储真实数据,末尾主动追加 \0,兼容 C 语言标准库函数。
  1. 极简 SDS(sdshdr5) 仅由 flagsbuf 组成,无 lenalloc 字段。flags 高 5 位存储字符串长度,仅用于长度小于 32的内部短字符串,极致压缩内存。
2.2.2 SDS 对比 C 语言原生字符串

C 语言字符串存在三大缺陷,也是 Redis 设计 SDS 的根本原因:

  1. 非二进制安全 :以 \0 作为字符串结束标记,若数据中包含 \0 会被直接截断,无法存储二进制文件;
  2. 获取长度效率低 :需要遍历整个字符数组直到 \0,时间复杂度 O (n);
  3. 内存操作低效:每次修改字符串都需要手动重新分配内存,频繁分配容易产生内存碎片,且无空间预分配机制。

针对以上问题,SDS 做了全面优化:

  • 依靠 len 字段标记数据长度,不再依赖 \0,实现二进制安全
  • 直接读取 len 获取长度,时间复杂度优化为 O (1);
  • 内存预分配 + 惰性缩容:字符串扩容时额外分配空闲空间,减少频繁内存重分配;缩容时不立即释放多余内存,留作后续复用,大幅提升性能。

2.3 String 三种上层编码

Redis 在 RedisObject 层面针对 String 做了三层编码优化,区分不同场景下的内存布局:

  1. int 编码 触发条件:Value 为纯整数。底层不创建 SDS 结构,直接将数字存入 RedisObject,无额外内存开销。
  2. embstr 编码 触发条件:字符串长度 ≤44 字节 。使用 sdshdr8,RedisObject 与 SDS 合并为一块连续内存,仅一次内存分配,指针开销小、CPU 缓存友好。该编码为只读模式,一旦执行修改操作,会自动转为 raw 编码。
  3. raw 编码 触发条件:字符串长度 >44 字节、embstr 被修改、int 转为字符串。RedisObject 与 SDS 分为两块独立内存,通过指针关联,支持动态扩容、长字符串存储与修改操作。

2.4 常用原生命令

命令 作用
SET key value 新增 / 覆盖键值对
GET key 根据 Key 获取 Value,键不存在返回空
APPEND key value 向字符串尾部追加内容,键不存在则等效 SET
STRLEN key 获取字符串长度,键不存在返回 0
MSET k1 v1 k2 v2... 批量设置多个键值对
MGET k1 k2... 批量获取多个 Key 的值

2.5 Spring Boot 实战代码

在 Spring Data Redis 中,通过 ValueOperations 操作 String 类型数据,RedisTemplate 会自动管理连接池,无需手动关闭连接。

复制代码
package com.qcby.springbootTest;

import org.junit.Test;
import org.junit.runner.RunWith;
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.SpringRunner;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@SpringBootTest
@RunWith(SpringRunner.class)
public class StringRedisTest {

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    @Test
    public void testBaseOperate() {
        // 获取String类型操作工具类
        ValueOperations<String, Object> valueOps = redisTemplate.opsForValue();

        // 基础 set / get
        valueOps.set("name", "minxr");
        String name = (String) valueOps.get("name");
        System.out.println("查询结果:" + name);

        // 字符串追加
        String oldValue = (String) valueOps.get("name");
        valueOps.set("name", oldValue + "jintao");
        System.out.println("追加后:" + valueOps.get("name"));

        // 覆盖数据
        valueOps.set("name", "jintao");
        System.out.println("覆盖后:" + valueOps.get("name"));

        // 删除Key
        redisTemplate.delete("name");
        System.out.println("删除后查询:" + valueOps.get("name"));

        // 批量存储
        Map<String, Object> multiMap = new HashMap<>();
        multiMap.put("name", "minxr");
        multiMap.put("jarorwar", "aaa");
        redisTemplate.opsForValue().multiSet(multiMap);

        // 批量获取
        List<Object> mgetResult = redisTemplate.opsForValue()
                .multiGet(Arrays.asList("name", "jarorwar"));
        System.out.println("批量查询:" + mgetResult);
    }

    @Test
    public void testKeyOperate() {
        // 清空当前数据库
        redisTemplate.getConnectionFactory().getConnection().flushDb();
        System.out.println("清空数据库成功");

        // 判断Key是否存在
        boolean exists = redisTemplate.hasKey("foo");
        System.out.println("foo是否存在:" + exists);

        // 存储数据
        redisTemplate.opsForValue().set("key", "values");
        exists = redisTemplate.hasKey("key");
        System.out.println("key是否存在:" + exists);
    }

    @Test
    public void testStringAll() throws InterruptedException {
        ValueOperations<String, Object> valueOps = redisTemplate.opsForValue();
        try {
            // 基础赋值取值
            valueOps.set("key", "Hello World!");
            System.out.println(valueOps.get("key"));
            redisTemplate.getConnectionFactory().getConnection().flushDb();

            // setIfAbsent 对应 SETNX 不存在则赋值
            valueOps.set("foo", "bar");
            Boolean setNx = valueOps.setIfAbsent("foo", "foo not exits");
            System.out.println("SETNX执行结果:" + setNx + ",当前值:" + valueOps.get("foo"));

            // 覆盖数据
            valueOps.set("foo", "foo update");
            System.out.println("覆盖后:" + valueOps.get("foo"));

            // 设置过期时间(2秒)
            valueOps.set("foo", "foo not exits", 2, java.util.concurrent.TimeUnit.SECONDS);
            System.out.println("设置过期后:" + valueOps.get("foo"));
            Thread.sleep(3000);
            System.out.println("3秒后查询:" + valueOps.get("foo"));

            // getAndSet 取值并覆盖
            valueOps.set("foo", "foo update");
            Object oldVal = valueOps.getAndSet("foo", "foo modify");
            System.out.println("原值:" + oldVal + ",新值:" + valueOps.get("foo"));

            // 批量操作
            Map<String, Object> msetMap = new HashMap<>();
            msetMap.put("mset1", "mvalue1");
            msetMap.put("mset2", "mvalue2");
            redisTemplate.opsForValue().multiSet(msetMap);
            List<Object> mgetList = valueOps.multiGet(Arrays.asList("mset1", "mset2"));
            System.out.println("批量查询:" + mgetList);

            // 批量删除
            Long delCount = redisTemplate.delete(Arrays.asList("foo", "foo1"));
            System.out.println("删除个数:" + delCount);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

代码说明

  • redisTemplate.opsForValue():获取 String 类型专属操作对象 ValueOperations
  • setIfAbsent:对应 Redis SETNX 命令,仅 Key 不存在时赋值,可用于分布式锁;
  • 所有方法均封装了原生 Redis 命令,连接池由框架自动管理。

三、Redis Hash 哈希类型

3.1 应用场景引入

在业务中,我们经常需要存储用户、商品、订单 等结构化数据。如果使用 String 存储 JSON 字符串,会存在明显缺陷:修改单个字段时,需要全量序列化、反序列化、网络传输,资源消耗大;多线程并发更新时还会出现更新丢失问题。

Redis Hash 类型正是为结构化对象存储 设计的数据结构,它是 Key-Field-Value 三级映射关系,一个 Redis Key 下可以维护多个 Field-Value 键值对,完美适配对象存储场景。

3.2 核心特性

  1. 结构:单 Key 对应多个 Field,Field 在当前 Hash 内唯一,Value 基于 SDS 实现,二进制安全;
  2. 容量:单个 Hash 最大支持 2^32 - 1 个字段;
  3. 原子性:单个字段的增删改查均为原子操作,天然规避并发更新问题;
  4. 内存优势:相比 JSON 字符串,无多余符号冗余,内存利用率更高;
  5. 时间复杂度:单字段操作 O (1),批量字段操作 O (N)(N 为操作字段数)。

3.3 底层实现

Hash 类型采用双结构自适应切换

  1. 压缩列表(ziplist) 默认优先使用,当 Hash 字段数量 ≤512 且单个 Value 长度 ≤64 字节时生效。采用连续内存存储,无指针开销,内存极度紧凑。
  2. 哈希表(hashtable) 当字段数或 Value 长度超过阈值,自动切换为哈希表。读写效率稳定 O (1),适合大数据量、高并发场景。

3.4 常用原生命令

命令 作用
HSET key field value 设置单个字段,新增返回 1,更新返回 0
HGET key field 获取单个字段值
HMSET / HMGET 批量设置 / 获取字段
HGETALL key 获取 Hash 所有字段与值
HSETNX key field value 字段不存在时才赋值
HDEL key field 删除指定字段
HLEN key 获取字段总数量

3.5 Spring Boot 实战代码

通过 HashOperations 操作 Hash 类型数据:

复制代码
@Test
public void testHash() {
    // 获取Hash操作对象
    HashOperations<String, String, String> hashOps = redisTemplate.opsForHash();

    // 批量设置字段
    Map<String, String> pairs = new HashMap<>();
    pairs.put("name", "Akshi");
    pairs.put("age", "2");
    pairs.put("sex", "Female");
    hashOps.putAll("kid", pairs);

    // 获取单个字段
    List<String> name = hashOps.multiGet("kid", Collections.singletonList("name"));
    System.out.println("姓名:" + name);

    // 删除字段
    hashOps.delete("kid", "age");
    System.out.println("pwd字段值:" + hashOps.multiGet("kid", Collections.singletonList("pwd")));

    // 获取字段数量
    System.out.println("字段总数:" + hashOps.size("kid"));
    // 获取所有字段名、字段值
    System.out.println("所有字段:" + hashOps.keys("kid"));
    System.out.println("所有值:" + hashOps.values("kid"));

    // 获取完整Hash数据
    Map<String, String> allPairs = hashOps.entries("kid");
    System.out.println("完整数据:" + allPairs);

    // 清空数据库
    redisTemplate.getConnectionFactory().getConnection().flushDb();

    // 新增Hash数据
    hashOps.put("hashs", "entryKey", "entryValue");
    hashOps.put("hashs", "entryKey1", "entryValue1");

    // 判断字段是否存在
    System.out.println("字段是否存在:" + hashOps.hasKey("hashs", "entryKey"));
    // 字段值自增
    Long incr = hashOps.increment("hashs", "entryKey", 123L);
    System.out.println("自增结果:" + incr);
}

代码说明

  • HashOperations 专门用于操作 Hash 结构,泛型依次对应:RedisKey、Field、Value;
  • Collections.singletonList:创建不可变单元素集合,常用于单字段查询;
  • increment 支持字段数字自增,原子性操作。

四、Redis List 列表类型

4.1 核心特性

Redis List 是有序、可重复 的字符串序列,基于双向链表语义实现,元素按照插入顺序排序,支持正向索引、反向索引。List 的优势集中在头尾增删元素,常用来实现消息队列、栈、时间线、分页列表等场景。

4.2 底层实现

Redis 版本不同,底层结构有差异:

  1. Redis 3.2 之前 双结构自适应:数据量小时使用 ziplist 压缩列表;数据量超标切换为双向链表
  2. Redis 3.2 及以上 统一使用 quicklist(快速列表),是双向链表 + 压缩列表的结合体。链表的每个节点都封装一个 ziplist,兼顾了链表头尾操作高效、ziplist 内存紧凑两大优点,是目前最优实现方案。

4.3 常用原生命令

表格

命令 作用
LPUSH / RPUSH 头部 / 尾部插入元素
LPOP / RPOP 头部 / 尾部弹出元素
LLEN key 获取列表长度
LRANGE key start stop 区间查询元素,0 -1 代表查询全部
LREM key count value 删除指定个数的指定元素
LSET key index value 修改指定索引元素
RPOPLPUSH src dst 元素从源列表尾部转移到目标列表头部

4.4 Spring Boot 实战代码

通过 ListOperations 操作 List 类型:

复制代码
@Test
public void testList() {
    String messagesKey = "messages";
    String listsKey = "lists";
    redisTemplate.delete(messagesKey);

    // 尾部插入元素
    redisTemplate.opsForList().rightPush(messagesKey, "Hello how are you?");
    redisTemplate.opsForList().rightPush(messagesKey, "Fine thanks.");
    List<Object> messages = redisTemplate.opsForList().range(messagesKey, 0, -1);
    System.out.println("消息列表:" + messages);

    // 清空数据库
    redisTemplate.getConnectionFactory().getConnection().flushDb();

    // 头部插入元素
    redisTemplate.opsForList().leftPush(listsKey, "vector");
    redisTemplate.opsForList().leftPush(listsKey, "ArrayList");
    redisTemplate.opsForList().leftPush(listsKey, "LinkedList");

    // 获取列表长度
    Long length = redisTemplate.opsForList().size(listsKey);
    System.out.println("列表长度:" + length);

    // 区间查询
    List<Object> rangeList = redisTemplate.opsForList().range(listsKey, 0, 3);
    System.out.println("0-3索引元素:" + rangeList);

    // 修改指定索引元素
    redisTemplate.opsForList().set(listsKey, 0, "hello list!");
    // 根据索引查询
    Object indexVal = redisTemplate.opsForList().index(listsKey, 1);
    System.out.println("索引1元素:" + indexVal);

    // 删除元素
    Long remove = redisTemplate.opsForList().remove(listsKey, 1, "vector");
    System.out.println("删除个数:" + remove);

    // 列表裁剪
    redisTemplate.opsForList().trim(listsKey, 0, 1);
    // 头部弹出
    Object pop = redisTemplate.opsForList().leftPop(listsKey);
    System.out.println("弹出元素:" + pop);

    List<Object> finalList = redisTemplate.opsForList().range(listsKey, 0, -1);
    System.out.println("最终列表:" + finalList);
}

五、Redis Set 集合类型

5.1 核心特性

Redis Set 是无序、元素唯一的字符串集合,天然去重,同时支持交集、并集、差集等数学集合运算。适用于点赞、收藏、好友列表、黑名单、去重统计等场景。

核心特点:元素无序、不可重复、所有操作原子性、支持集合运算。

5.2 底层实现

采用双结构自适应切换:

  1. 整数集合(intset) 集合元素全为 64 位整数,且元素数量 ≤512 时使用。连续内存结构,二分查找,内存占用极低。
  2. 哈希表(hashtable) 包含非整数元素,或元素数量超标时切换。元素作为哈希表 Key,Value 为空占位,保证增删查 O (1)。

注意:intset 转 hashtable 是单向不可逆的。

5.3 常用原生命令

SADD 添加元素、SREM 删除元素、SMEMBERS 获取所有元素、SISMEMBER 判断元素是否存在、SINTER/SUNION/SDIFF 集合交并差运算。

六、Redis ZSet 有序集合类型

6.1 核心特性

ZSet(Sorted Set)有序集合,结合了 Set 唯一性排序能力。每个成员(member)关联一个浮点型分值(score),根据 score 自动排序,是 Redis 中功能强大的排序结构,常用于排行榜、延时队列、带权重的排序场景。

排序规则:优先按 score 升序排列;score 相同时,按 member 字典序排序。 时间复杂度:核心操作 O (logN),范围查询 O (logN + K)。

6.2 常用原生命令

ZADD 添加元素、ZSCORE 查询分值、ZRANGE/ZREVRANGE 升序 / 降序查询、ZINCRBY 分值自增、ZRANK/ZREVRANK 查询排名。

6.3 Spring Boot 实战代码

复制代码
@Test
public void sortedSet() {
    String hackersKey = "hackers";
    String zsetKey = "zset";

    // 添加有序集合元素
    redisTemplate.opsForZSet().add(hackersKey, "Alan Kay", 1940);
    redisTemplate.opsForZSet().add(hackersKey, "Richard Stallman", 1953);
    redisTemplate.opsForZSet().add(hackersKey, "Alan Turing", 1912);

    // 升序查询
    Set<Object> ascSet = redisTemplate.opsForZSet().range(hackersKey, 0, -1);
    System.out.println("升序排行:" + ascSet);
    // 降序查询
    Set<Object> descSet = redisTemplate.opsForZSet().reverseRange(hackersKey, 0, -1);
    System.out.println("降序排行:" + descSet);

    // 清空数据库
    redisTemplate.getConnectionFactory().getConnection().flushDb();
    // 新增元素
    redisTemplate.opsForZSet().add(zsetKey, "hello", 10.1);
    redisTemplate.opsForZSet().add(zsetKey, "zset", 9.0);

    // 元素个数
    Long count = redisTemplate.opsForZSet().size(zsetKey);
    System.out.println("元素总数:" + count);
    // 查询分值
    Double score = redisTemplate.opsForZSet().score(zsetKey, "zset");
    System.out.println("zset分值:" + score);
}

七、Redis 通用 Key 操作命令

除五大数据类型专属命令外,Redis 所有 Key 都支持通用操作,日常使用频率极高:

  1. EXPIRE key seconds:设置 Key 过期时间(秒);
  2. TTL key:查询剩余过期时间;
  3. PERSIST key:移除过期时间,转为永久 Key;
  4. DEL key:删除 Key;
  5. EXISTS key:判断 Key 是否存在;
  6. TYPE key:查看 Key 对应的数据类型;
  7. KEYS pattern:通配符查询 Key(生产环境慎用)。

通用操作实战:

复制代码
@Test
public void testKeyCommon() throws InterruptedException {
    Set<String> allKeys = redisTemplate.keys("*");
    System.out.println("所有Key:" + allKeys);

    // 设置过期时间
    redisTemplate.opsForValue().set("timekey", "min", 10, TimeUnit.SECONDS);
    Long ttl = redisTemplate.getExpire("timekey");
    System.out.println("剩余过期时间:" + ttl);

    Thread.sleep(5000);
    System.out.println("休眠后过期时间:" + redisTemplate.getExpire("timekey"));

    // 重命名Key
    if (redisTemplate.hasKey("timekey")) {
        redisTemplate.rename("timekey", "time");
    }
    System.out.println("原Key值:" + redisTemplate.opsForValue().get("timekey"));
    System.out.println("新Key值:" + redisTemplate.opsForValue().get("time"));
}

八、五大基础数据类型总结与场景选型

  1. String:最简单通用,缓存普通文本、数字、计数器、简单对象;
  2. Hash:存储结构化对象(用户、商品),频繁修改单个字段的场景;
  3. List:有序可重复,消息队列、时间线、分页、栈结构;
  4. Set:无序唯一,去重、点赞、好友、集合运算;
  5. ZSet:有序唯一,各类排行榜、权重排序、延时队列。

五大类型底层均基于 ziplist、hashtable、quicklist、intset、SDS 等基础结构组合实现,Redis 根据数据特征自动切换存储结构,在内存占用和运行性能之间做到极致平衡。

九、结尾

Redis 五大基础数据类型是学习和使用 Redis 的基石,不仅要熟练掌握命令与代码调用,更要理解底层设计原理。在实际开发中,根据业务场景合理选择数据类型,能够大幅提升系统性能、简化代码逻辑。本文从原理、命令、实战三个维度完整讲解了五大类型,可作为日常开发、面试复习的参考资料。

相关推荐
weixin_397574094 分钟前
用自然语言查数据库出图表靠谱吗?一次智能问数实践复盘
数据库
字节跳动开源2 小时前
Viking AI 搜索 CLI 正式发布:会说话,就能做搜索推荐
数据库·人工智能·开源
TechWJ3 小时前
数据库在公司内网,出差路上想查数据怎么办?
服务器·数据库·mariadb
我是一颗柠檬3 小时前
【MySQL全面教学】MySQL事务与ACID Day9(2026年)
数据库·后端·mysql
橙子圆1233 小时前
Redis知识9之集群
数据库·redis·缓存
鱼鳞_3 小时前
苍穹外卖-Day08(缓存套餐)
java·redis·缓存
BlackHeart12033 小时前
【SQL】Oracle中序列(Sequence)作为默认值引发的ORA-00979
数据库·sql·oracle
苏渡苇4 小时前
服务容错的必要性与Spring Cloud Alibaba Sentinel 限流配置实战
spring boot·spring cloud·sentinel
bug菌4 小时前
【SpringBoot 3.x 第254节】夯爆了,数据库访问性能优化实战详解!
数据库·spring boot·后端
xxl大卡4 小时前
MySQL的执行流程
数据库·mysql