
一、什么是 RedisRedis 是一个开源的、基于内存的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 内存 ↓ 快速读取 ↓ 高性能而 MySQLMySQL 磁盘 ↓ 持久化存储 ↓ 数据可靠Redis 虽然也提供 RDB、AOF 等持久化机制但它的核心优势依然是内存中的高速访问。2.2 数据结构不同MySQL 是典型的关系型数据库。数据通常以数据库 ↓ 表 ↓ 行 ↓ 字段的形式组织。例如user id name age 1 张三 20 2 李四 22Redis 则是 Key-Value 模型key value user:1001 张三 user:1002 李四并且 Redis 的 Value 并不只是简单的字符串还可以是StringListSetHashSorted Set等多种数据结构。2.3 性能不同Redis 数据主要在内存中操作因此读写速度非常快。MySQL 查询通常涉及应用程序 ↓ MySQL ↓ 磁盘 / Buffer Pool ↓ 查询而 Redis应用程序 ↓ Redis ↓ 内存因此对于大量频繁读取的数据Redis 通常比 MySQL 更适合作为缓存。2.4 使用场景不同MySQL 更适合用户信息商品信息订单支付记录交易数据财务数据需要长期保存的数据Redis 更适合热点数据登录 Session验证码排行榜计数器分布式锁高频访问数据例如电商网站商品详细信息 ↓ MySQL ↓ 持久化保存 热门商品 ↓ Redis ↓ 快速访问2.5 Redis 和 MySQL 对比对比项RedisMySQL类型Key-Value 数据库关系型数据库主要存储内存磁盘查询速度非常快相对较慢数据结构丰富表结构持久化支持支持事务能力相对简单强数据可靠性相对较弱较强主要用途缓存、高性能读写核心业务数据数据量受内存限制通常更大典型场景缓存、排行榜、Session用户、订单、商品所以面试中可以总结为MySQL 更擅长数据的持久化和复杂查询Redis 更擅长高性能的数据读写和缓存。实际项目中通常采用 Redis MySQL 的组合通过 Redis 减少 MySQL 的访问压力提高系统整体性能。三、Redis 的数据类型Redis 最常见的五种基础数据类型StringHashListSetSorted SetZSet四、StringString 是 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实现简单的分布式锁。五、HashHash 可以理解成Redis 中的一个对象。例如用户user:1001 name → 张三 age → 20 gender → 男RedisHSET 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 保存完整 JSONHash 可以单独修改某一个字段。例如只修改年龄HSET user:1001 age 21六、ListList 是一个有序的字符串列表。可以理解为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从而实现简单的消息队列。七、SetSet 是一个无序且元素唯一的集合。例如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 SetZSetSorted Set也叫 ZSet。它和 Set 最大的区别是ZSet 中每个元素都有一个 Score并且会根据 Score 排序。例如张三 100 李四 80 王五 90Redis 会按照 Score 进行排序。可以表示张三 100 王五 90 李四 80ZSet 的应用场景ZSet 最经典的场景就是排行榜。例如游戏排行榜玩家 分数 张三 1000 李四 950 王五 900 赵六 850RedisZADD ranking 1000 张三 ZADD ranking 950 李四 ZADD ranking 900 王五然后可以按照分数进行排序。因此排行榜是 Redis ZSet 最经典的应用场景之一。九、Redis 五种数据类型总结类型特点常见应用String字符串、数字缓存、计数器、分布式锁HashKey-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没有然后返回不存在如果攻击者不断请求不存在的 ID999999 888888 777777 666666 ...每次都会Redis → 没有 ↓ MySQL → 没有最终大量无效请求会直接打到 MySQL。这就是缓存穿透。十二、缓存穿透的解决方法常见有两种方法。方法一缓存空对象当 MySQL 查询不到数据时也把这个结果缓存起来。例如查询 user:999999MySQL不存在Redisuser: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分钟 随机时间例如商品A30分钟 商品B32分钟 商品C35分钟 商品D38分钟这样缓存会逐渐失效而不是在同一时间大量失效。方法二热点数据永不过期对于非常重要的热点数据可以不设置 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 时重点掌握五种数据类型以及缓存穿透、缓存击穿、缓存雪崩三个经典问题和对应的解决方案。