Redis 学习笔记
更新: 11/3/2025 字数: 0 字 时长: 0 分钟
Redis(Remote Dictionary Server)是一个开源的、使用 C 语言编写的、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。
特点
- 速度快:数据存储在内存中,读写速度非常快。
- 持久化:支持 RDB 和 AOF 两种持久化方式,保证数据在服务重启后不丢失。
- 丰富的数据类型:支持 String、Hash、List、Set、Sorted Set 等多种数据类型。
- 原子性操作:所有操作都是原子性的,支持事务。
- 发布/订阅:支持发布/订阅模式,可以用于消息队列。
- 高可用:支持主从复制和哨兵(Sentinel)机制,实现高可用。
应用场景
缓存
- 场景:加速热点数据查询,减轻数据库压力。
- 流程:
- 客户端请求数据,先查询 Redis。
- 如果命中(数据存在),直接返回结果。
- 如果未命中(数据不存在),则查询数据库,将结果写入 Redis 并设置过期时间,然后返回给客户端。
分布式 Session
- 场景:在分布式或集群环境下,多台应用服务器需要共享用户的登录状态。
- 方案:将用户的 Session 信息存储在 Redis 中,所有服务器都从 Redis 获取 Session,从而实现共享。
分布式锁
- 场景:保证在分布式系统中,多个服务节点访问共享资源时的数据一致性和原子性。
- 方案:利用 Redis 的
SETNX(SET if Not eXists) 等原子命令来实现。
其他
- 消息队列:利用 List 或 Stream 数据结构实现简单的消息队列。
- 排行榜:利用 Sorted Set 的分数排序功能实现实时排行榜。
- 限流:利用 String 或 Hash 结构,结合过期时间,实现接口访问频率限制。
- 实时计数:利用
INCR等原子操作,实现高并发下的实时计数,如点赞数、浏览量。
数据类型
String(字符串)
- 介绍:最基本的数据类型,可以存储字符串、整数或浮点数。
- 常用命令:
SET key value:设置指定 key 的值。GET key:获取指定 key 的值。INCR key:将 key 中储存的数字值增一。DECR key:将 key 中储存的数字值减一。
Hash(哈希)
- 介绍:一个 string 类型的 field 和 value 的映射表,特别适合用于存储对象。
- 常用命令:
HSET key field value:将哈希表 key 中的字段 field 的值设为 value。HGET key field:获取存储在哈希表中指定字段的值。HGETALL key:获取在哈希表中指定 key 的所有字段和值。
List(列表)
- 介绍:简单的字符串列表,按照插入顺序排序。可以添加一个元素到列表的头部(左边)或者尾部(右边)。
- 常用命令:
LPUSH key value1 [value2]:将一个或多个值插入到列表头部。RPUSH key value1 [value2]:将一个或多个值插入到列表尾部。LPOP key:移出并获取列表的第一个元素。RPOP key:移出并获取列表的最后一个元素。LRANGE key start stop:获取列表指定范围内的元素。
Set(集合)
- 介绍:String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。
- 常用命令:
SADD key member1 [member2]:向集合添加一个或多个成员。SMEMBERS key:返回集合中的所有成员。SISMEMBER key member:判断 member 元素是否是集合 key 的成员。
Sorted Set(有序集合)
- 介绍:和 Set 一样也是 string 类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个 double 类型的分数。redis 正是通过分数来为集合中的成员进行从小到大的排序。
- 常用命令:
ZADD key score1 member1 [score2 member2]:向有序集合添加一个或多个成员,或者更新已存在成员的分数。ZRANGE key start stop [WITHSCORES]:通过索引区间返回有序集合成指定区间内的成员。ZREVRANGE key start stop [WITHSCORES]:返回有序集中指定区间内的成员,通过索引,分数从高到低。
持久化
RDB (Redis Database)
- 介绍:在指定的时间间隔内生成数据集的时间点快照。
- 优点:
- 适合用于备份。
- 恢复速度快。
- 缺点:
- 如果服务器宕机,会丢失最后一次快照之后的数据。
AOF (Append Only File)
- 介绍:以日志的形式记录服务器所处理的每一个写操作。
- 优点:
- 数据更完整,丢失数据的风险更小。
- 缺点:
- 文件体积更大。
- 恢复速度比 RDB 慢。
缓存常见问题
缓存穿透
- 现象:查询一个数据库和缓存中都不存在的数据,导致每次请求都会直接查询数据库,给数据库带来压力。
- 解决方案:
- 缓存空对象:当查询数据库为空时,将一个空对象缓存起来,并设置一个较短的过期时间。
- 布隆过滤器:在访问缓存之前,通过布隆过滤器判断 key 是否存在。如果不存在,直接返回,避免查询数据库。
缓存击穿
- 现象:某个热点 key 在失效的瞬间,有大量的并发请求访问这个 key,导致这些请求都直接打到数据库上。
- 解决方案:
- 互斥锁:在查询数据库之前,先获取一个互斥锁。只有获取到锁的线程才能查询数据库,并将结果写入缓存,其他线程则等待。
- 热点数据永不过期:对于热点数据,可以设置为永不过期,或者在后台异步更新。
缓存雪崩
- 现象:在某个时间点,缓存中大量的 key 同时过期,导致大量的请求直接打到数据库上,造成数据库压力过大甚至宕机。
- 解决方案:
- 随机化过期时间:在设置 key 的过期时间时,增加一个随机值,避免大量 key 在同一时间失效。
- 高可用缓存:搭建 Redis 集群,提高缓存的可用性。
- 服务降级/熔断:当数据库压力过大时,可以暂时牺牲非核心业务,直接返回预定义的值或者错误信息。
Java 操作 Redis
在 Java 中操作 Redis 有多种客户端和框架可供选择,它们各有优缺点。
Jedis
- 介绍:一个非常老牌的 Redis Java 客户端,直连 Redis Server,API 丰富。
- 优点:简单易用,API 与 Redis 命令基本保持一致。
- 缺点:同步阻塞 IO,线程不安全,需要通过连接池来管理连接,性能相对较低。
Lettuce
- 介绍:一个可伸缩的线程安全的 Redis 客户端,基于 Netty 实现,支持同步、异步和响应式编程。
- 优点:
- 线程安全:单个连接可以被多个线程共享。
- 高性能:基于 Netty 的异步和非阻塞 IO。
- 响应式支持:天然支持响应式编程模型。
- 缺点:API 学习成本相对 Jedis 较高。
Spring Data Redis
- 介绍:Spring 社区提供的 Redis 集成解决方案,它在 Lettuce 和 Jedis 之上做了一层封装,提供了统一的 API。
- 优点:
- 与 Spring 生态无缝集成。
- 提供了
RedisTemplate等高度封装的模板类,简化了 Redis 操作。 - 支持底层客户端(Jedis/Lettuce)的切换。
- 缺点:需要引入 Spring 框架。
Redisson
- 介绍:一个功能强大的 Redis Java 客户端,实现了分布式和可扩展的 Java 数据结构。
- 优点:
- 提供了丰富的分布式对象,如
Map,Set,List,Lock,Semaphore等。 - 实现了分布式锁,易于使用。
- 支持 Redis 集群、哨兵、主从等多种部署模式。
- 提供了丰富的分布式对象,如
- 缺点:相对较重,学习成本较高。
JetCache
- 介绍:一个基于 Java 的缓存系统封装,提供了统一的 API 和注解来简化缓存的使用。它不仅仅是针对 Redis。
- 优点:
- 统一 API:支持多种缓存实现(Redis, Caffeine, ...),可以轻松切换。
- 注解支持:通过
@Cached和@CacheUpdate等注解,可以非常方便地为方法添加缓存逻辑。 - 自动刷新:支持缓存的自动刷新和加载。
- 缺点:更侧重于缓存应用层,而不仅仅是 Redis 客户端。
缓存预热
- 原理:系统上线后或低峰期,提前将可能的热点数据加载到缓存中。这样可以避免在用户请求高峰期,因缓存中无数据而导致大量请求直接访问数据库,从而造成数据库压力过大。
- 实现方式:通常通过定时任务来实现,在系统负载较低时执行。常见的定时任务框架有:
- Spring Scheduler:Spring 框架自带的轻量级定时任务工具。
- Quartz:一个功能强大的开源作业调度框架。
- XXL-Job:一个分布式任务调度平台。
- 优点:
- 提升用户首次访问速度和体验。
- 保护数据库,防止服务启动或流量高峰时被打垮。
- 缺点:
- 增加系统启动时间或开发复杂度。
- 预热的数据若不是真正的热点,会造成缓存资源浪费。
Key 设计原则
良好的 Key 设计可以提高可读性、可管理性,并节省内存。
- 可读性与可管理性:Key 应该具有自描述性,方便定位和排查问题。推荐使用统一的命名规范,例如
业务名:表名:唯一标识,如user:info:1001。 - 简洁性:在保证可读性的前提下,Key 应该尽可能简短,以节省宝贵的内存空间。
- 唯一性:必须保证 Key 的全局唯一性,避免不同业务的数据相互覆盖。
- 避免特殊字符:不要在 Key 中使用空格、换行符等特殊字符,以免引起不必要的解析错误。
Key 过期策略
为什么需要设置过期时间
为 Key 设置过期时间是 Redis 作为缓存使用的核心机制之一。
- 释放内存资源:Redis 数据存储在内存中。如果不设置过期时间,数据会持续占用内存,最终可能导致内存耗尽(OOM)。设置过期时间可以让不再需要的数据自动被删除,回收内存。
- 保证数据一致性:缓存中的数据是数据库中数据的副本。源数据更新后,缓存若不更新就会产生数据不一致。通过设置过期时间,可以定期淘汰旧数据,当下次访问时重新从数据库加载新数据,保证数据的最终一致性。
- 清理冷数据:长期不被访问的数据(冷数据)会占用内存。过期机制可以有效清理这些数据,为热点数据腾出空间。
不设置过期时间会怎么样
- 数据永不过期:如果没有为 Key 设置过期时间(或设置为 -1),那么这个 Key 将会变成永久有效(persistent) 的,除非被程序显式删除(例如使用
DEL命令)。 - 依赖内存淘汰策略:当 Redis 内存达到上限(
maxmemory)时,会触发内存淘汰策略来删除一些 Key 以释放空间。如果此时一个永久 Key 被选中,它也可能被删除。如果淘汰策略设置为noeviction(默认策略),则在内存满时,所有写操作都会报错,导致服务不可用。
生产环境 Redis 必须考虑的问题
断电或宕机导致数据丢失
为了防止因服务器断电或宕机导致内存数据丢失,Redis 提供了两种持久化机制:
RDB (Redis Database Backup)
- 原理:在指定的时间间隔内,将内存中的数据集生成一个时间点快照(snapshot),并将其写入磁盘。
- 优点:恢复速度快,文件紧凑,适合用于数据备份。
- 缺点:如果服务器在两次快照之间宕机,会丢失最后一次快照之后的所有更改。
AOF (Append Only File)
- 原理:以日志的形式,记录下服务器收到的每一个写操作命令。当服务器重启时,会重新执行 AOF 文件中的命令来恢复数据。
- 优点:数据安全性更高,丢失数据的风险极小。
- 缺点:文件体积通常比 RDB 大,恢复速度相对较慢。
企业级方案:通常采用 RDB + AOF 混合使用的策略,结合两者的优点,既保证了数据恢复的效率,又提高了数据的安全性。
Redis 服务器挂了怎么办
采用主从架构(Master-Slave)进行读写分离和数据备份。
- 主节点(Master):负责处理所有写操作(如
SET,DEL)和部分读操作。当数据发生变化时,主节点会将写命令异步复制给所有从节点。 - 从节点(Slave):负责备份主节点的数据,并处理大部分读操作,分担主节点的读取压力。
- 优点:当主节点宕机时,从节点可以被提升为新的主节点,保证服务的高可用性。
主从切换靠谁决定
主从架构本身无法实现自动故障转移,需要哨兵(Sentinel)机制来监控和管理。
- 监控:Sentinel 是一个独立的进程,它会持续监控主从节点的健康状态。
- 自动故障转移:当 Sentinel 集群判断主节点下线时,会自动从从节点中选举一个新的主节点。
- 通知:Sentinel 会将新的主节点地址通知给客户端,实现服务的无缝切换。
数据量太大,单机放不下
主从架构解决了高可用性问题,但所有数据仍然存储在一个主节点上,无法解决单机容量瓶颈。
当数据量大到单机内存无法容纳时,就需要使用 Redis 集群(Cluster)。
- 数据分片:集群模式将数据自动分割成多个部分(分片),并将这些分片存储在不同的物理节点上。
- 哈希槽(Hash Slot):Redis Cluster 预设了 16384 个哈希槽。当存入一个 Key 时,会根据 Key 的 CRC16 算法计算出一个值,然后对 16384 取模,决定该 Key 存储在哪一个槽中。每个 Redis 节点负责管理一部分哈希槽。
- 优点:通过增加节点可以实现系统的水平扩展,从而提升存储容量、分担读写压力、提高并发能力。
Redis 压力太大怎么办
当单个 Redis 实例或集群面临过高的并发压力时,可以从架构层面进行优化:
引入多级缓存
- 架构:在应用服务器内部增加一层本地缓存(如 Caffeine、Guava Cache),形成
本地缓存 → Redis 缓存 → 数据库的访问顺序。 - 原理:对于访问频率极高的数据,直接从本地缓存返回,避免了网络开销,进一步减轻 Redis 的压力。
- 架构:在应用服务器内部增加一层本地缓存(如 Caffeine、Guava Cache),形成
缓存预热
- 原理:在系统启动或低峰期,提前将可预见的热点数据加载到 Redis 缓存中。
- 目的:避免在流量高峰期,因大量缓存未命中而导致请求全部穿透到数据库,从而保护后端系统。
总结
| 面临问题 | 解决方案 | 核心技术 |
|---|---|---|
| 数据丢失 | 数据备份与恢复 | RDB + AOF |
| 服务宕机 | 主从备份,自动故障转移 | 主从复制 + 哨兵 |
| 容量瓶颈 | 数据分片,水平扩展 | Redis Cluster |
| 并发压力 | 读写分离,多级缓存 | 主从架构 |
