一、什么是 Redis?
Redis 是一个开源的、基于内存的Key-Value 数据库,全称是Remote Dictionary Server。
与传统的 MySQL 不同,Redis 的数据主要存储在内存中,因此具有非常高的读写性能,常被用于:
缓存
Session 存储
排行榜
计数器
分布式锁
消息队列
热点数据存储
在实际项目中,Redis 通常不会完全替代 MySQL,而是和 MySQL 配合使用。
一个比较典型的架构是:
用户请求 ↓ 后端服务器 ↓ ┌─────────┐ │ Redis │ └─────────┘ ↓ ↓ 命中 未命中 ↓ ↓ 返回数据 MySQL ↓ 查询数据 ↓ 写入 Redis ↓ 返回也就是说:
MySQL 负责持久化存储,Redis 负责高速缓存。
二、Redis 与 MySQL 的区别
Redis 和 MySQL 都可以存储数据,但是它们的设计目标完全不同。
2.1 存储方式不同
Redis 是基于内存进行数据存储的。
MySQL 主要是基于磁盘进行持久化存储。
例如:
Redis 内存 ↓ 快速读取 ↓ 高性能而 MySQL:
MySQL 磁盘 ↓ 持久化存储 ↓ 数据可靠Redis 虽然也提供 RDB、AOF 等持久化机制,但它的核心优势依然是内存中的高速访问。
2.2 数据结构不同
MySQL 是典型的关系型数据库。
数据通常以:
数据库 ↓ 表 ↓ 行 ↓ 字段的形式组织。
例如:
user id name age 1 张三 20 2 李四 22Redis 则是 Key-Value 模型:
key value user:1001 张三 user:1002 李四并且 Redis 的 Value 并不只是简单的字符串,还可以是:
String
List
Set
Hash
Sorted Set
等多种数据结构。
2.3 性能不同
Redis 数据主要在内存中操作,因此读写速度非常快。
MySQL 查询通常涉及:
应用程序 ↓ MySQL ↓ 磁盘 / Buffer Pool ↓ 查询而 Redis:
应用程序 ↓ Redis ↓ 内存因此对于大量频繁读取的数据,Redis 通常比 MySQL 更适合作为缓存。
2.4 使用场景不同
MySQL 更适合:
用户信息
商品信息
订单
支付记录
交易数据
财务数据
需要长期保存的数据
Redis 更适合:
热点数据
登录 Session
验证码
排行榜
计数器
分布式锁
高频访问数据
例如电商网站:
商品详细信息 ↓ MySQL ↓ 持久化保存 热门商品 ↓ Redis ↓ 快速访问2.5 Redis 和 MySQL 对比
| 对比项 | Redis | MySQL |
|---|---|---|
| 类型 | Key-Value 数据库 | 关系型数据库 |
| 主要存储 | 内存 | 磁盘 |
| 查询速度 | 非常快 | 相对较慢 |
| 数据结构 | 丰富 | 表结构 |
| 持久化 | 支持 | 支持 |
| 事务能力 | 相对简单 | 强 |
| 数据可靠性 | 相对较弱 | 较强 |
| 主要用途 | 缓存、高性能读写 | 核心业务数据 |
| 数据量 | 受内存限制 | 通常更大 |
| 典型场景 | 缓存、排行榜、Session | 用户、订单、商品 |
所以面试中可以总结为:
MySQL 更擅长数据的持久化和复杂查询,Redis 更擅长高性能的数据读写和缓存。实际项目中通常采用 Redis + MySQL 的组合,通过 Redis 减少 MySQL 的访问压力,提高系统整体性能。
三、Redis 的数据类型
Redis 最常见的五种基础数据类型:
String
Hash
List
Set
Sorted Set(ZSet)
四、String
String 是 Redis 最基本的数据类型。
格式:
key → value例如:
name → 张三Redis 命令:
SET name 张三 GET name结果:
张三4.1 String 的应用场景
缓存对象
例如:
user:1001 → {"id":1001,"name":"张三","age":20}可以将 JSON 字符串直接存储到 Redis。
计数器
例如文章阅读量:
article:view:1001 → 100每访问一次:
INCR article:view:1001变成:
101所以 Redis 的 String 非常适合做:
阅读量
点赞数
访问次数
库存数量
等计数。
分布式锁
String 还可以配合:
SET key value NX EX实现简单的分布式锁。
五、Hash
Hash 可以理解成:
Redis 中的一个对象。
例如用户:
user:1001 name → 张三 age → 20 gender → 男Redis:
HSET user:1001 name 张三 HSET user:1001 age 20 HSET user:1001 gender 男查询:
HGET user:1001 name得到:
张三Hash 的应用场景
Hash 非常适合保存对象。
例如:
user:1001 ↓ name = 张三 age = 20 phone = 138xxxx相比于直接使用 String 保存完整 JSON,Hash 可以单独修改某一个字段。
例如只修改年龄:
HSET user:1001 age 21六、List
List 是一个有序的字符串列表。
可以理解为:
List A B C DRedis 支持从左边或者右边插入数据。
例如:
LPUSH message A LPUSH message B LPUSH message C可以形成:
C B AList 的应用场景
List 可以用于:
消息队列
最新消息列表
时间线
简单队列
例如:
消息队列 Redis List ---------------- 消息1 消息2 消息3 消息4 ----------------生产者负责添加:
LPUSH消费者负责获取:
RPOP从而实现简单的消息队列。
七、Set
Set 是一个无序且元素唯一的集合。
例如:
set: Java Redis MySQL JavaScript如果重复添加:
Java Java Redis最终只会保存:
Java RedisSet 的应用场景
共同好友
例如:
张三好友: A B C D 李四好友: B C E F使用 Set 可以求交集:
B C也就是两个人的共同好友。
标签
例如一个用户拥有:
Java Redis MySQL Spring Boot可以使用 Set 存储用户标签。
八、Sorted Set(ZSet)
Sorted Set,也叫 ZSet。
它和 Set 最大的区别是:
ZSet 中每个元素都有一个 Score,并且会根据 Score 排序。
例如:
张三 100 李四 80 王五 90Redis 会按照 Score 进行排序。
可以表示:
张三 100 王五 90 李四 80ZSet 的应用场景
ZSet 最经典的场景就是:
排行榜。
例如游戏排行榜:
玩家 分数 张三 1000 李四 950 王五 900 赵六 850Redis:
ZADD ranking 1000 张三 ZADD ranking 950 李四 ZADD ranking 900 王五然后可以按照分数进行排序。
因此:
排行榜是 Redis ZSet 最经典的应用场景之一。
九、Redis 五种数据类型总结
| 类型 | 特点 | 常见应用 |
|---|---|---|
| String | 字符串、数字 | 缓存、计数器、分布式锁 |
| Hash | Key-Value 对象 | 用户信息、对象 |
| List | 有序列表 | 消息队列、时间线 |
| Set | 无序、唯一 | 标签、共同好友 |
| ZSet | 有序、唯一、带 Score | 排行榜 |
面试可以直接回答:
Redis 常见的数据类型有 String、Hash、List、Set 和 Sorted Set。String 常用于缓存和计数器,Hash 常用于存储对象,List 可以实现简单消息队列,Set 适合去重和集合运算,ZSet 最经典的应用场景是排行榜。
十、什么是 Redis 缓存?
在实际项目中,我们经常会遇到一个问题:
如果每一次用户请求都查询 MySQL,那么当用户数量非常大的时候:
100万请求 ↓ 100万次 MySQL 查询 ↓ MySQL 压力巨大因此我们可以加入 Redis:
用户 ↓ Redis ↓ 命中? ↓ ├── 是 → 直接返回 │ └── 否 ↓ MySQL ↓ 查询数据 ↓ 写入 Redis ↓ 返回数据这样就可以减少 MySQL 的访问压力。
但是,使用 Redis 作为缓存以后,会出现三个非常经典的问题:
缓存穿透
缓存击穿
缓存雪崩
这三个问题也是 Redis 面试中的高频考点。
十一、缓存穿透
11.1 什么是缓存穿透?
缓存穿透指的是:
请求查询一个 Redis 和 MySQL 中都不存在的数据,导致请求每次都绕过 Redis,直接访问 MySQL。
例如:
用户请求: id = 999999999Redis:
没有继续查询 MySQL:
没有然后返回:
不存在如果攻击者不断请求不存在的 ID:
999999 888888 777777 666666 ...每次都会:
Redis → 没有 ↓ MySQL → 没有最终大量无效请求会直接打到 MySQL。
这就是:
缓存穿透。
十二、缓存穿透的解决方法
常见有两种方法。
方法一:缓存空对象
当 MySQL 查询不到数据时,也把这个结果缓存起来。
例如:
查询: user:999999MySQL:
不存在Redis:
user:999999 → null下一次请求:
user:999999直接从 Redis 读取:
null就不会继续访问 MySQL。
一般会给空值设置一个较短的过期时间,例如:
60秒避免大量不存在的数据长期占用 Redis。
方法二:布隆过滤器
布隆过滤器是一种:
空间效率非常高的概率型数据结构。
可以在访问 Redis 和 MySQL 之前先判断:
请求 ↓ 布隆过滤器 ↓ 这个数据可能存在吗? ↓ ├── 一定不存在 → 直接返回 │ └── 可能存在 ↓ Redis ↓ MySQL例如数据库中只有:
1 2 3 4 5用户请求:
999999布隆过滤器可以判断:
一定不存在那么就可以直接拒绝请求。
需要注意
布隆过滤器存在一个特点:
可能误判存在,但不会把存在判断成不存在。
所以它适合用来判断:
一定不存在然后直接拦截。
十三、缓存击穿
13.1 什么是缓存击穿?
缓存击穿和缓存穿透很容易混淆。
缓存击穿指的是:
一个热点数据突然过期,在同一时间大量请求访问这个热点数据,导致大量请求同时访问 MySQL。
例如:
Redis: hot:user:1001这是一个非常热门的数据。
假设:
10000 个用户同时访问这个数据。
正常情况下:
10000 请求 ↓ Redis ↓ 命中 ↓ 直接返回但是如果这个热点数据突然过期:
Redis ↓ 缓存失效此时:
10000 请求 ↓ ↓ ↓ ↓ ↓ 同时访问 MySQLMySQL 瞬间承受巨大的压力。
这就是:
缓存击穿。
十四、缓存击穿的解决方法
方法一:互斥锁
当发现缓存不存在时,不让所有请求同时查询 MySQL,而是只允许一个请求去查询。
例如:
请求A 请求B 请求C 请求D ↓ 发现 Redis 没有数据 ↓ 加锁 ↓ 请求A获得锁 ↓ 查询MySQL ↓ 写入Redis ↓ 释放锁其他请求:
请求B 请求C 请求D ↓ 等待 ↓ Redis重新查询 ↓ 获得数据最终:
只有一个请求访问 MySQL从而避免大量请求同时击穿数据库。
方法二:逻辑过期
另一种方式是:
缓存本身不设置真正的过期时间,而是在数据内部保存一个逻辑过期时间。
例如:
{ "data": "商品信息", "expireTime": "10:00:00" }当请求发现:
当前时间 > expireTime说明数据逻辑上已经过期。
但是:
仍然可以先返回旧数据。
然后后台异步更新缓存。
流程:
请求 ↓ Redis ↓ 发现逻辑过期 ↓ 返回旧数据 ↓ 后台线程更新 ↓ MySQL ↓ 重新写入 Redis这样用户不会因为缓存更新而大量等待 MySQL。
十五、缓存雪崩
15.1 什么是缓存雪崩?
缓存雪崩指的是:
大量缓存数据在同一时间失效,导致大量请求直接访问数据库,从而造成数据库压力瞬间暴增。
例如:
系统缓存了:
商品1 商品2 商品3 商品4 ... 商品10000如果这些数据都是:
10:00同时过期。
那么:
10:00 ↓ 大量 Redis Key 同时失效 ↓ 大量请求无法命中 Redis ↓ 大量请求访问 MySQL ↓ MySQL 压力暴增 ↓ 甚至数据库宕机这就是:
缓存雪崩。
十六、缓存击穿和缓存雪崩的区别
这是面试中非常容易问的问题。
缓存击穿
重点:
一个热点 Key 过期。
例如:
热门商品 ↓ 缓存过期 ↓ 大量请求同时访问 MySQL缓存雪崩
重点:
大量 Key 同时过期。
例如:
商品1 商品2 商品3 ... 商品10000 同时过期 ↓ 大量请求访问 MySQL可以记忆为:
穿透:不存在 击穿:一个热点 Key 雪崩:大量 Key十七、缓存雪崩的解决方法
方法一:设置随机过期时间
不要让所有缓存同时过期。
例如原本:
所有缓存: TTL = 30分钟可以改成:
TTL = 30分钟 + 随机时间例如:
商品A:30分钟 商品B:32分钟 商品C:35分钟 商品D:38分钟这样缓存会逐渐失效,而不是在同一时间大量失效。
方法二:热点数据永不过期
对于非常重要的热点数据,可以不设置 Redis 的物理过期时间。
通过:
逻辑过期或者后台定时任务主动更新。
例如:
热门商品 热门排行榜 热门文章这些数据可以提前刷新。
方法三:Redis 集群
如果单个 Redis 节点出现问题,可以使用 Redis Cluster 等方案进行分布式部署。
例如:
Redis Cluster / | \ Node1 Node2 Node3通过集群提高:
可用性
承载能力
数据分布能力
方法四:服务降级
当 Redis 或 MySQL 出现异常时,可以进行服务降级。
例如:
正常: Redis ↓ MySQL ↓ 返回完整数据异常情况下:
Redis异常 ↓ 返回默认数据或者:
暂时关闭非核心功能从而保证核心业务继续运行。
十八、缓存三大问题总结
| 问题 | 原因 | 典型情况 | 解决方案 |
|---|---|---|---|
| 缓存穿透 | 查询不存在的数据 | Redis没有,MySQL也没有 | 布隆过滤器、缓存空值 |
| 缓存击穿 | 热点Key突然过期 | 一个热点Key大量请求 | 互斥锁、逻辑过期 |
| 缓存雪崩 | 大量Key同时过期 | 大量缓存同时失效 | 随机TTL、逻辑过期、集群、降级 |
可以用一句话记忆:
缓存穿透: 查不存在的数据 缓存击穿: 一个热点Key失效 缓存雪崩: 大量Key同时失效十九、Redis + MySQL 的经典缓存架构
实际项目中经常使用:
用户请求 ↓ 后端服务 ↓ 查询 Redis ↓ ┌────┴────┐ ↓ ↓ 命中 未命中 ↓ ↓ 返回数据 查询 MySQL ↓ 获取数据 ↓ 写入 Redis ↓ 返回代码逻辑可以简单理解为:
String data = redis.get(key); if (data != null) { return data; } data = mysql.query(key); if (data != null) { redis.set(key, data); } return data;实际项目还需要进一步考虑:
缓存穿透 缓存击穿 缓存雪崩 缓存与数据库数据一致性 缓存过期时间 Redis 高可用 Redis 持久化二十、面试总结
如果面试官问:
“你了解 Redis 吗?”
可以这样回答:
Redis 是一个基于内存的 Key-Value 数据库,主要特点是读写速度快、支持丰富的数据结构,因此在实际项目中经常作为缓存使用。Redis 常见的数据类型包括 String、Hash、List、Set 和 Sorted Set。
Redis 通常和 MySQL 配合使用,MySQL 主要负责核心业务数据的持久化存储,而 Redis 主要负责热点数据缓存,从而减少 MySQL 的访问压力,提高系统性能。
使用 Redis 做缓存时,需要重点考虑缓存穿透、缓存击穿和缓存雪崩三个问题。缓存穿透是查询不存在的数据,可以通过缓存空值或者布隆过滤器解决;缓存击穿是热点 Key 失效导致大量请求同时访问数据库,可以通过互斥锁或者逻辑过期解决;缓存雪崩是大量缓存 Key 同时失效,可以通过设置随机过期时间、逻辑过期、Redis 集群和服务降级等方式解决。
二十一、最终记忆图
Redis │ ┌──────────────┼──────────────┐ │ │ │ 数据类型 Redis缓存 MySQL │ │ │ ┌────┼────┐ │ 持久化存储 │ │ │ │ String Hash List │ │ │ │ │ Set ZSet │ │ ┌───────────┼───────────┐ │ │ │ 穿透 击穿 雪崩 │ │ │ 不存在数据 热点Key 大量Key │ │ │ 布隆过滤器 互斥锁 随机TTL 缓存空值 逻辑过期 逻辑过期 集群/降级一句话总结
Redis 的核心价值就是利用内存和丰富的数据结构提供高性能的数据访问能力;MySQL 负责可靠持久化,Redis 负责高速缓存。使用 Redis 时重点掌握五种数据类型,以及缓存穿透、缓存击穿、缓存雪崩三个经典问题和对应的解决方案。