Redis学习笔记

知识分子没文化
2023-09-25 / 0 评论 / 2,030 阅读 / 6,805 字数 / 正在检测是否收录...

目录:

Redis 官网:Redis - The Real-time Data Platform

一、Redis的基础知识

1.1、什么是Redis

Redis(REmote DIctionary Server)是一个开源的、基于内存的、键值对(Key-Value)数据库,它支持多种数据结构,并提供原子操作、事务、Lua脚本、发布订阅等高级特性。

Redis 支持的基础数据类型有五种:

数据类型 介绍 底层实现 特点
String(字符串) 最基础、最常用的数据类型 SDS(简单动态字符串) 可以存储:字符串、整数、浮点数、二进制数据
单个数据最大支持 512MB
支持原子操作(如自增、自减)
List(列表) 本质是一个双向链表 压缩列表 / 双向链表 有序、可重复
支持从两端插入/弹出
操作时间复杂度:头尾 O(1)
Hash(哈希) 类似于 Map<String, String> 压缩列表 / 哈希表 存储键值对集合(field-value)
适合存储对象
支持单字段读写
Set(集合) 无序、不 哈希表 / 整数集合 自动去重
支持集合运算(交集、并集、差集)
ZSet(Sorted Set,有序集合) 在 Set 基础上增加了一个 score(分数) 用来排序 跳跃表 + 哈希表 元素唯一,但 score 可重复
按 score 自动排序(底层是跳跃表 + 哈希表)
查询效率高(范围查询)

还有几种基于基础数据类型的扩展类型:

数据类型 介绍 特点
Bitmap(位图) 基于 String 的按位操作 每一位是 0 或 1
非常节省空间(适合大规模布尔状态)
HyperLogLog 用于基数统计(去重计数)的概率算法 占用极小内存(≈12KB)
有一定误差(约 0.81%)
Geospatial(地理位置) 用于存储地理位置信息 基于 Sorted Set 实现
支持距离计算、范围搜索
Stream(流,Redis 5+) 类似消息队列,但功能更强 支持消费组(Consumer Group)
支持消息持久化
可回溯消息

1.2、为什么要有Redis

传统关系型数据库(如 MySQL)擅长复杂查询和事务,但磁盘 I/O 存在瓶颈导致高并发下存在延迟高、吞吐低的问题,因此数据库的读写性能往往会成为瓶颈。

而加入 Redis 作为高性能缓存,将使用频率高的数据(热点数据)放在内存中,需要时可以直接通过请求 Redis 获取,只有当 Redis 获取不到时才会查询数据库,这种方式可以降低数据库的压力,同时还显著提升系统的响应速度和吞吐量。

Redis 的主要优势包括:

  • 基于内存:所有数据存储在内存中,读写也基于内存操作,避免了磁盘 I/O 瓶颈,因此速度极快,这是最根本的原因;

  • 单线程模型(核心I/O线程)

    • 准确来讲,Redis 不是完全单线程,Redis 6.0 之后,网络I/O、持久化会使用多线程,但命令执行依然是单线程
    • 在进行读写操作时,单线程可以避免多线程的上下文切换和竞争(锁竞争、死锁)带来的额外开销,反而提升了处理速度,此时的瓶颈不在CPU,而在内存和网络;
    • 并且由于单线程没有了复杂的同步机制,因此也不易出错;
  • I/O多路复用机制:让单线程能同时监听多个 socket 连接,当某个连接有数据请求时,才去处理。类似于一个服务员同时服务很多桌客人,谁举手就去谁那。使用了epoll等高效机制;

  • 高效的数据结构:底层针对不同场景设计了极致优化的数据结构。

二、内存管理与淘汰机制

Redis 是内存数据库,随着应用的运行,如果不清理,Redis 中的 Key 会不断增加,最终内存耗尽导致 OOM(Out Of Memery)。

针对于内存溢出问题,Redis 通过内存过期策略预防内存溢出,而当内存使用达到 maxmemory 上限导致新 Key 无法写入时,Redis 使用内存淘汰机制淘汰旧数据为新数据腾出空间。

2.1、内存过期策略

Redis 通过为 Key 设置过期时间(TTL)来管理内存中的数据生命周期。当 Key 的过期时间到达时,Redis 会自动删除该键及其对应的值,以避免旧数据的无限累计。

内存过期策略主要包括:

  • 定时删除:为每个设置过期时间的 Key 维护一个定时器,当过期时间到达时立即删除。但这种方式会消耗大量的CPU资源,比较影响性能。
  • 惰性删除:在访问 Key 时才检查其是否过期,如果过期则删除。这种方式不会额外消耗CPU资源,但可能导致内存中存在大量过期的 Key,占用内存空间。

需要说明的是,Redis 并不是在 Key 到期的一瞬间就立刻将其从内存中删除,而是采用了定时删除与惰性删除两种策略的组合:

  • Redis 内部开了一个定时任务,默认每段时间(比如 100ms)运行一次;
  • 从设置了过期时间(TTL)的 Key 中随机抽取 20 个数据进行检查;
  • 如果这 20 个 Key 中过期的 Key 占比超过 25%(即超过 5 个),Redis 会认为当前内存中过期 Key 较多,不会等待下一个 100ms,而是立即重复上一个步骤继续抽查、继续清理,直到过期的 Key 占比低于 25%。

内存过期策略也有可能会导致一个问题,如果同一时刻大量数据过期或者 Redis 无法提供服务,客户端请求从 Redis 中获取不到已过期的数据就会查询数据库,从而使数据库请求激增,压力过大造成数据库服务器宕机,即缓存雪崩

所以要避免大量数据在同一时刻过期,解决办法是在固定过期时间上增加一个随机偏移量,同时对于热点数据,不设置过期时间,使其达到“物理”上的永不过期,可以避免缓存击穿问题。

2.2、内存淘汰机制

当内存使用达到 maxmemory 上限,且新数据写入时,触发淘汰策略,根据配置的内存淘汰策略删除部分键值对以释放内存。

那么应该选择哪些数据淘汰,大致的思想有两种:

  • LRU(Least Recently Used)是按照最近最少使用原则来筛选数据,即最不常用的数据会被筛选出来;

  • LFU(Least Frequently Used)是 Redis4 新增的淘汰策略,它根据 key 的最近访问频率进行淘汰,即访问次数最少的数据会被筛选出来。

再结合淘汰数据的范围(全部 Key 或设置了过期时间的 Key),那么综合考虑下来内存淘汰策略就有八种:

策略 描述 适用场景
noeviction 默认策略。不淘汰,写请求直接返回错误(如SET失败) 数据绝不能丢的场景,如作为数据库
allkeys-random 从所有key中随机淘汰 缓存数据访问频率相近
allkeys-lru 从所有数据中淘汰最近最少使用(LRU)的key 通用缓存,希望保留最可能被再次访问的数据
allkeys-lfu(Redis4.0) 从所有数据中淘汰使用频率最低(LFU)的key 通用缓存,希望保留经常被访问的数据
volatile-ttl 从设置了过期时间的key中,淘汰将要过期的key 通用缓存,希望优先淘汰快过期的数据
volatile-random 从设置了过期时间的key中,随机淘汰key 缓存数据访问频率相近
volatile-lru 从设置了过期时间的数据中淘汰最近最少使用(LRU)的key 通用缓存,希望保留最可能被再次访问的数据
volatile-lfu(Redis4.0) 从设置了过期时间的数据中淘汰使用频率最低(LFU)的key 通用缓存,希望保留经常被访问的数据,比LRU更精准

结合内存过期策略来说,如果此时设置的淘汰策略是 allkeys-lruallkeys-random,那么即使某个 Key 还没到过期时间,也可能被提前淘汰,使用 volatile-lruvolatile-ttl 策略,即只针对有过期时间的 Key 进行淘汰,就可以解决这个问题。

三、持久化

对于 Redis 来说,由于数据都在内存中,如果发生断电或进程崩溃时就必然面临数据丢失的风险,因此有了数据的持久化,以便在 Redis 重启后能恢复数据

Redis 支持的持久化方式有三种:RDB 持久化AOF 持久化RDB-AOF 混合持久化

3.1、RDB 持久化

RDB(Redis Database)是 Redis 默认采用的持久化方式,它的原理是 Redis 在后台 fork(复制)一个子进程,子进程将该时间的内存数据生成快照文件写入到磁盘中,创建一个经过压缩的 “.rdb” 二进制文件,存储各个数据库的键值对数据等信息。

RDB 持久化的触发方式有两种:

  • 手动触发:通过 SAVE 或 BGSAVE 命令触发 RDB 持久化操作,创建“.rdb”文件;
  • 自动触发:通过配置选项,让服务器在满足指定条件时自动执行 BGSAVE 命令。

RDB 持久化的优缺点:

  • 优点:RDB生成紧凑压缩的二进制文件,体积小,恢复数据的速度非常快;
  • 缺点:RDB 持久化没办法做到实时,BGSAVE 每次运行都要执行 fork 操作创建子进程,属于重量级操作,不宜频繁执行。

3.2、AOF 持久化

AOF 持久化的原理是记录了每次写入命令到 “.aof” 格式的文本文件,类似MySQL的binlog,当 Redis 重启时再重新执行 AOF 文件中的命令来恢复数据到内存中。

AOF的工作流程包括:命令写入(append)、文件同步(sync)、文件重写(rewrite)、重启加载(load)。

需要控制命令同步到硬盘的频率,即多久将命令写入到 AOF 文件中一次,就是同步策略 (appendfsync):

  • appendfsync always:每次写操作后立即同步到磁盘。最安全,但性能最差;
  • appendfsync everysec:每秒同步一次。折中方案,默认推荐,最多丢1秒数据,性能和安全平衡最好;
  • appendfsync no:由操作系统决定同步时机。最快,但最不安全,但可能丢大量数据。

AOF 重写(Rewrite)机制:为了解决文件越来越大的问题,Redis会自动(或手动 BGREWRITEAOF)在后台重写 AOF 文件。只保留“当前数据集的最小命令集合”,去掉冗余操作(比如多次 SET 同一个 Key,只留最后一次)。重写操作由子进程完成,不会阻塞主进程。

AOF 持久化的优缺点:

  • 优点:

    • 数据安全性高,最多丢失一秒的数据;
    • 生成的 “.aof” 格式的日志文件可读,便于调试和修复。
  • 缺点:

    • 文件通常比RDB大,恢复数据慢(因为需要逐条重新执行命令);

    • 对性能有一定影响(尤其是 always 策略)。

3.3、RDB-AOF 混合持久化(Redis 4.0)

RDB 持久化和 AOF 持久化都有各自鲜明的优缺点,因此从 Redis4.0 开始引入集成二者优点的持久化方式:RDB-AOF 混合持久化,这种方式的原理是将内存数据先以 RDB 格式写入一个快照到 AOF 文件头部,后续再把写命令追加到快照后面,恢复时先恢复数据的快照,再执行后面的追加命令。

与 AOF 持久化类似,RDB-AOF 混合持久化也具备同步策略重写机制

优势:兼顾了 RDB 持久化的加载速度和 AOF 持久化的数据安全,是 Redis 生产环境的首选方案。

四、Redis集群方案

4.1、单机模式(Standalone)

一台服务器上运行一个Redis实例(redis-server)。数据全部放在一台机器的内存里,所有读写操作都经过这个实例,没有任何备份或扩展。

优点:部署简单,适合开发、测试、小型项目;且性能最好,因为没有网络复制开销

缺点:机器宕机、Redis进程崩溃,都会导致服务完全不可用;数据量超过单机内存或 QPS 过高,就扛不住。

4.2、主从复制(Replication)

一个主节点(Master)负责处理写请求,多个从节点(Replica / Slave)实时复制主节点(Master)的数据,只负责处理读请求。整个集群的数据流向是单向的:Master -> Slave。

优点:结构简单,配置容易;从节点分担主节点读压力,极大提升 QPS

缺点:所有写操作都依赖主节点,写性能上限受限于主节点,无法水平扩展写能力;主节点故障需要人工干预切换,无法自动故障转移;异步复制可能导致轻微数据不一致

4.3、哨兵模式(Redis Sentinel)

哨兵模式基于主从模式扩展,主要解决主从模式中主节点(Master)宕机无法自动恢复的问题,以实现高可用。

在主从架构的基础上,引入一组独立的 Sentinel(哨兵)节点。Sentinel 不处理业务读写,只负责心跳检测主从节点,当主节点故障(客观下线),哨兵通过 Raft 协议投票选出一个新的主节点,并通知客户端和其他从节点。

主观下线与客观下线:一个 Sentinel 认为节点挂了是主观下线,半数以上 Sentinel 认为挂了才是客观下线,避免网络误判。

哨兵节点的数量至少为 3 个,且为奇数个,以避免脑裂问题,总体架构推荐至少 3 Sentinel + 1 Master + 2+ Replica。

优点:保留了主从模式的优点,依然支持读写分离,从节点分担读压力;实现了故障自动检测和切换

缺点:本质上还是主从模式,写性能上限受限于主节点,依然无法水平扩展写能力;且主从切换过程中,Redis 会短暂不可用

4.4、Redis Cluster

Redis Cluster 是 Redis 官方提供的分布式解决方案,自 Redis 3.0 引入,旨在通过分片(Sharding)技术将数据分布在多个主节点上,解决单机写瓶颈和内存容量瓶颈。

Redis Cluster 将整个数据集分成 16384 个哈希槽(Hash Slot)。每个键通过 CRC16(key) % 16384 计算后,决定落在哪个槽位。集群中存在多个主节点(Master),每个主节点负责一段连续的槽位区间(如 0-5000、5001-10000、10001-16383),并且可挂载一个或多个从节点(Replica)作为数据备份实现高可用。节点之间通过 Gossip 协议相互通信,自动发现新节点、传播故障信息、协调槽位迁移。客户端连接任意一个节点,若请求的数据不在当前节点,节点会返回正确的目标节点地址(MOVED 重定向),智能客户端根据新的目标节点地址自动跳转到正确的节点。

优点

  • 数据容量水平扩展:总数据量不再受限于单机内存,集群总容量随节点增加而线性增长
  • 写性能水平扩展:写操作分散到多个主节点,增加节点即可线性提升写吞吐量,彻底突破单机写瓶颈
  • 内置高可用:主节点故障自动转移,无需人工干预,比哨兵 Sentinel 更快
  • 故障隔离更好:单个节点故障只影响部分数据,只影响它负责的槽位,其他数据仍可正常读写

缺点

  • 复杂度高:搭建、监控、运维更麻烦(需要处理 slot 迁移、热点 slot 等)
  • 客户端要求高:必须用支持Cluster的客户端(Redisson、go-redis等),否则需要处理MOVED重定向
  • 批量操作受限:不支持跨节点的事务、和多 Key 操作(除非 Key 带有相同的 {hashtag},强制将其分配到同一槽位)
  • 资源消耗更高:至少6个节点起步
  • 数据倾斜风险:热点key可能集中在某个节点,需要优化key设计

五、应用场景

5.1 数据库

Redis 可以作为主数据库使用,适用于对性能要求高、数据量相对较小的场景,如会话存储、实时排行榜等。

5.2 系统缓存

Redis 最常见的用途是作为缓存层,通过缓存热点数据,可以减少对数据库的查询次数,减轻数据库负担,显著提高应用访问速度。

5.3 消息队列

Redis 的列表和发布/订阅模式可以用于实现消息队列:

  • 通过列表(List)的 LPUSH 和 RPOP 命令可以实现简单的消息队列功能
  • 通过发布/订阅模式可以实现更复杂的消息分发和订阅功能

5.4 计数器和排行榜

Redis 的原子操作和有序集合(ZSet)非常适合实现计数器和排行榜功能,通过为每个元素关联一个分数,可以方便地实现排序和统计功能,如网站访问统计、游戏积分榜等。

5.5 分布式锁

Redis 的原子性操作可以用于实现分布式锁。通过 SETNX 命令可以尝试获取锁,如果获取成功则执行临界区代码;执行完毕后通过 DEL 命令释放锁,保证在分布式环境下的互斥访问。为了避免死锁,还可以为锁设置过期时间。

六、缓存相关问题

在高并发系统中,使用 Redis 做缓存时,最容易引发系统故障的三大“杀手”就是缓存穿透缓存击穿缓存雪崩

6.1、缓存穿透

客户端请求的数据在 Redis 中不存在(缓存未命中),同时在数据库中也不存在。每次请求都会直接打到数据库。如果是恶意构造的大量无效请求(比如 userId = -1、随机长字符串、已删除的商品 ID),数据库瞬间就被打崩。

核心特点

  • 查询的是“根本不存在”的数据。
  • 缓存和数据库都无法命中,请求每次都穿透。
  • 极易被恶意攻击利用(也叫恶意穿透)。

常见场景

  • 接口未做参数校验,攻击者用不存在的 ID 疯狂刷接口。
  • 查询已下架或从未存在过的商品 / 用户详情。

解决方式

  • 接口层参数校验(第一道防线):对请求参数做基础合法性检查(格式、长度等),提前过滤无效请求
  • 缓存空对象(空值缓存)(最常用、简单有效):当数据库返回 null/空时,也在 Redis 中缓存一个空结果,并设置较短的过期时间(建议 30 秒 ~ 5 分钟,避免长期占用内存)
  • 布隆过滤器(Bloom Filter):在 Redis 前加一层布隆过滤器,提前判断该 Key 是否可能存在,如果肯定不存在,直接返回 null,不查 Redis 和数据库,如果可能存在,再走正常缓存流程。

6.2、缓存击穿

某个高并发热点 Key 的缓存突然过期(或被删除),而此时有大量并发请求同时到达。所有请求发现缓存 miss,同时去查数据库,导致数据库瞬时压力暴增,甚至宕机。

核心特点

  • 问题发生在单个热点 Key 过期瞬间
  • 查询的是真实存在非常热门的数据。
  • 影响虽然是单个 Key,但并发量极大,破坏力强。

常见场景

  • 秒杀商品的库存/详情 Key 刚好过期。
  • 热门新闻、明星话题、爆款商品的缓存到期。
  • 大促期间某个热卖品的缓存失效。

解决方式(核心:只让一个请求去查数据库,其他等待或用旧数据):

  • 缓存未命中时,先尝试获取分布式锁。只有拿到锁的线程才允许查询数据库,其他线程等待或短暂重试。

  • 不依赖 Redis 的原生 expire,而是把“过期时间”字段存到 Value 里(例如 JSON 中加一个 expireTime)。用户请求时,如果发现逻辑已过期,后台异步线程去刷新数据,用户先返回旧数据

  • 热点数据永不过期 + 后台刷新 对极重要热点 Key 不设过期时间,或设很长 TTL,由定时任务/消息队列后台定期刷新

6.3、缓存雪崩

大量缓存 Key 在同一时间段内集体过期(或 Redis 实例宕机),导致海量请求同时穿透到数据库,数据库压力骤增,甚至引发连锁反应导致系统雪崩。

核心特点

  • 影响的是大量 Key(而非单个)。
  • 通常发生在同一时刻大量 Key 同时失效。
  • 后果最严重,可能导致整个服务不可用。

常见场景

  • 所有 Key 统一设置相同过期时间(如全设 30 分钟)。
  • 大促活动结束,大量活动缓存同时到期。
  • Redis 重启或故障(无持久化时,所有缓存瞬间丢失)。

解决方式

  • 最简单、最有效:设置过期时间时加上随机偏移,避免大量 Key 同时到期
  • 缓存预热 + 永不过期策略:提前把热点数据加载到缓存;核心数据可永不过期,由后台异步更新
  • 多级缓存:第一层用 Caffeine/Guava 本地缓存挡住大部分请求,Redis 作为二级,数据库作为最后防线
  • 熔断降级 + 限流 + 快速失败:使用 Sentinel / Hystrix / Resilience4j 等框架:当数据库压力过大时,快速熔断,返回兜底数据(“系统繁忙,请稍后重试”或缓存的旧数据)

七、SpringBoot3集成

7.1、导入Maven依赖

Spring Boot 3 默认使用 Lettuce 作为 Redis 客户端,只需引入 spring-data-redis:

<!-- spring-data-redis -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

若要使用连接池以提升性能,额外添加 commons-pool2

<!-- 为 Redis 客户端(Lettuce)提供连接池功能 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-pool2</artifactId>
</dependency>

7.2、配置文件

spring:
  data:
    redis:
      host: localhost
      port: 6379
      password:        # 有密码则填写
      database: 0
      timeout: 3000ms
      lettuce:
        pool:
          max-active: 8      # 最大连接数
          max-idle: 8        # 最大空闲连接数
          min-idle: 0        # 最小空闲连接数
          max-wait: -1ms     # 获取连接最大等待时间,-1表示无限等待(永不超时)

7.3、测试连接

@SpringBootTest
class RedisTest {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Test
    void testRedis() {
        redisTemplate.opsForValue().set("name", "张三");
        String value = (String) redisTemplate.opsForValue().get("name");
        System.out.println(value);
    }

}

7.4、自定义RedisTemplate

Redis 只能存储字节数组(byte[]),无论是对象还是字符串,进入 Redis 前都必须序列化。默认的 RedisTemplate 默认提供的是 JdkSerializationRedisSerializer,它会把对象转成 Java 原生的二进制序列化流。虽然功能上没问题,但有两个明显缺点:

  • 可读性差:在 Redis 里看到的是乱码,无法直接通过 redis-cli 调试。
  • 耦合性强:只能用 Java 客户端读取,其他语言很难介入。

因此项目一般会使用 StringRedisSerializerJackson2JsonRedisSerializer 改为 JSON 序列化,从而让存储的内容清晰可读,并且跨语言兼容。

创建一个配置类 “RedisConfig”

@Configuration
public class RedisConfig {

    @Bean
    public StringRedisTemplate redisTemplate(RedisConnectionFactory factory) {
        StringRedisTemplate redisTemplate = new StringRedisTemplate();
        redisTemplate.setConnectionFactory(factory);

        // 使用 String 序列化 key
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        // Key 序列化
        redisTemplate.setKeySerializer(stringSerializer);
        // HashKey
        redisTemplate.setHashKeySerializer(stringSerializer);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
        // 使用 Jackson 序列化 value
        Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<>(objectMapper, Object.class);
        // Value 序列化
        redisTemplate.setValueSerializer(jacksonSerializer);
        // HashValue
        redisTemplate.setHashValueSerializer(jacksonSerializer);
        return redisTemplate;
    }

}

7.5、常用操作

官方API文档:Overview (Spring Data Redis API)

7.5.1、通用操作(Key 级别)

这些操作不关心 Value 的类型,只针对键本身。

操作 原生命令 RedisTemplate 方法
判断键是否存在 EXISTS key redisTemplate.hasKey(key)
删除键 DEL key redisTemplate.delete(key)
设置过期时间 EXPIRE key seconds redisTemplate.expire(key, timeout, unit)
查看剩余过期时间 TTL key redisTemplate.getExpire(key, unit
查找匹配的键(慎用) KEYS pattern redisTemplate.keys(pattern)
重命名键 RENAME key newkey redisTemplate.rename(oldKey, newKey)
获取键类型 TYPE key redisTemplate.type(key)
7.5.2、字符串 (String) 操作

最基础的类型,可以存字符串、数字、甚至序列化后的对象。

操作 原生命令 ValueOperations 方法 (opsForValue())
设置值 SET key value set(key, value)
获取值 GET key get(key)
设置值并指定过期时间 SETEX key seconds value set(key, value, timeout, unit)
仅在键不存在时设置 SETNX key value setIfAbsent(key, value)
批量设置 MSET key1 v1 key2 v2 multiSet(map)
批量获取 MGET key1 key2 multiGet(keys)
数字递增/递减 INCR key / DECR key increment(key) / decrement(key)
追加字符串 APPEND key value append(key, value)
获取字符串长度 STRLEN key size(key)

典型场景:缓存 token、计数器、分布式锁(利用 setIfAbsent)。

7.5.3、哈希 (Hash) 操作

适合存储对象(如用户信息),字段可单独读写。

操作 原生命令 HashOperations 方法 (opsForHash())
设置单个字段 HSET key field value put(key, hashKey, value)
获取单个字段 HGET key field get(key, hashKey)
批量设置字段 HMSET key f1 v1 f2 v2 putAll(key, map)
批量获取字段 HMGET key f1 f2 multiGet(key, hashKeys)
获取所有字段和值 HGETALL key entries(key)
删除字段 HDEL key field delete(key, hashKeys)
判断字段是否存在 HEXISTS key field hasKey(key, hashKey)
获取所有字段名 HKEYS key keys(key)
获取所有值 HVALS key values(key)
数字递增 HINCRBY key field incr increment(key, hashKey, delta)

典型场景:购物车(用户ID为key,商品ID为field,数量为value)、缓存对象的部分更新。

7.5.4、列表 (List) 操作

双向链表,支持从左右两端压入弹出,可用作队列或栈。

操作 原生命令 ListOperations 方法 (opsForList())
左/右推入 LPUSH key val / RPUSH key val leftPush(key, val) / rightPush(key, val)
左/右弹出 LPOP key / RPOP key leftPop(key) / rightPop(key)
范围获取 LRANGE key start stop range(key, start, end)
修剪列表 LTRIM key start stop trim(key, start, end)
获取长度 LLEN key size(key)
按索引获取 LINDEX key index index(key, index)
移除指定值 LREM key count val remove(key, count, value)
阻塞弹出 BLPOP key timeout leftPop(key, timeout, unit)

典型场景:消息队列(leftPush + rightPop)、最新评论列表、异步任务队列。

7.5.5、集合 (Set) 操作

无序、不重复的元素集合,支持集合间的交/并/差运算。

操作 原生命令 SetOperations 方法 (opsForSet())
添加元素 SADD key member add(key, values)
移除元素 SREM key member remove(key, values)
获取所有成员 SMEMBERS key members(key)
判断是否成员 SISMEMBER key member isMember(key, obj)
随机弹出 SPOP key pop(key)
获取集合大小 SCARD key size(key)
交集 SINTER key1 key2 intersect(key, otherKey)
并集 SUNION key1 key2 union(key, otherKey)
差集 SDIFF key1 key2 difference(key, otherKey)

典型场景:标签系统、共同好友、用户去重。

7.5.6. 有序集合 (Sorted Set) 操作

每个成员关联一个分数(score),按分数自动排序,支持按排名、分数范围获取。

操作 原生命令 ZSetOperations 方法 (opsForZSet())
添加元素 ZADD key score member add(key, value, score)
按排名范围取 ZRANGE key start stop range(key, start, end)
反向范围取 ZREVRANGE key start stop reverseRange(key, start, end)
按分数范围取 ZRANGEBYSCORE key min max rangeByScore(key, min, max)
获取成员分数 ZSCORE key member score(key, obj)
获取成员排名 ZRANK key member rank(key, obj)
移除元素 ZREM key member remove(key, values)
获取大小 ZCARD key size(key)
增加分数 ZINCRBY key incr member incrementScore(key, value, delta)

典型场景:排行榜(积分排名)、延时队列(按时间戳排序)。

7.5.6、RedisTemplate与StringRedisTemplate的区别

RedisTemplateStringRedisTemplate 的核心区别在于泛型定义与默认序列化策略,这直接决定了数据在 Redis 中的存储形态、空间占用以及跨语言的通用性。

具体来说,RedisTemplate 默认的泛型定义为 RedisTemplate<Object, Object>,其底层采用的是 JdkSerializationRedisSerializer(JDK 原生序列化)。这种机制会将 Java 对象及其实例化元数据(如类路径、版本号等)整体转化为二进制字节数组。因此,写入 Redis 的数据会带有大量诸如 \xac\xed\x00\x05 的二进制前缀,导致在 redis-cli 终端中完全不可读。此外,这种序列化方式不仅额外占用了较多内存空间,而且由于深度绑定了 Java 语言特性,导致 Go、Python 或 Node.js 等跨语言技术栈极难解析,极易形成数据孤岛。

相比之下,StringRedisTemplateRedisTemplate<String, String> 的直接子类。它在初始化时,就已经将 Key、Value 以及 Hash 键值的所有序列化器统一指定为了 StringRedisSerializer(标准 UTF-8 字符串序列化)。因为只处理纯文本,所以它在 Redis 中存储的数据清晰直观、完全可读,且没有任何内存空间浪费。更重要的是,它存入的标准字符串(如 JSON 文本)具备极完美的跨语言通用性

在现代高并发分布式架构中,往往更推荐使用 StringRedisTemplate。即在业务代码中,由研发人员通过 Jackson 或 Fastjson2 将 Java 对象显式地手动转化为 JSON 字符串再进行读写。这样既能保证高可读性与跨语言消费能力,又能彻底避免因 Java 侧类结构重构、包路径修改而导致的 Redis 反序列化崩溃风险。

参考资料:

Java21 + SpringBoot3整合Redis - 程序员偏安 - 博客园

Spring-Data-Redis 官方文档

Spring-Data-Redis 非官方中文文档

3

评论 (0)

取消