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