【关于redis高性能,高可用处理】

Redis 之所以强大,很大程度上归功于它丰富且高效的数据结构。Redis 的数据结构可以分为 5 种基础数据结构多种高级/特殊数据结构

以下是详细的分类、常用命令以及实际业务中的使用场景:


一、 五大基础数据结构

1. String(字符串)

最简单、最基础的数据类型。它是二进制安全的,除了存字符串,还可以存整数、浮点数,甚至存图片/Base64编码的文件(但一般不推荐存大文件)。底层实现是 SDS(简单动态字符串)。

  • 常用命令SET, GET, INCR, DECR, EXPIRE, SETNX
  • 使用场景
    • 常规缓存:缓存 HTML 页面、JSON 数据、配置信息。
    • 计数器 :利用 INCR 实现文章阅读量、视频播放量、商品点赞数的原子递增。
    • 分布式锁 :利用 SET key value NX EX(不存在才设置,并加上过期时间)实现分布式锁。
    • 分布式 Session:将用户的 Session 信息集中存储在 Redis 中,实现多节点 Session 共享。
2. Hash(哈希/字典)

一个键值对(field-value)的集合,非常适合用来存储对象 。相比用 String 存一大段 JSON,Hash 的优势在于可以只修改或获取对象中的某个字段,且更省内存。

  • 常用命令HSET, HGET, HMSET, HMGET, HDEL, HINCRBY
  • 使用场景
    • 对象存储:存储用户信息(User ID -> {name, age, email})、商品信息。
    • 购物车Key 为用户 ID,Field 为商品 ID,Value 为商品数量。方便单独修改某个商品的数量。
    • 用户画像/属性:存储用户的各种标签和属性值。
3. List(列表)

有序的字符串列表,支持从两端(头部或尾部)推入和弹出元素。底层通常是 Quicklist(双向链表+压缩列表)。

  • 常用命令LPUSH, RPUSH, LPOP, RPOP, LRANGE, BLPOP (阻塞弹出)
  • 使用场景
    • 消息队列(轻量级) :利用 LPUSH 生产消息,BRPOP(阻塞读取)消费消息,实现简单的异步任务处理。
    • 最新文章/列表 :利用 LPUSH 插入新数据,LRANGE 0 9 获取最新 10 条数据(如最新 10 条评论、朋友圈时间线)。
    • 分页查询:对于数据量不大且不需要复杂排序的列表,可以用 List 做简单的分页。
4. Set(集合)

无序的、唯一的字符串集合。底层是 intset(整数集合)或 hashtable。支持数学上的集合运算(交、并、差)。

  • 常用命令SADD, SREM, SISMEMBER, SMEMBERS, SINTER (交集), SUNION (并集), SDIFF (差集), SRANDMEMBER (随机获取)
  • 使用场景
    • 标签(Tags) :给文章或商品打标签,如 article:1:tags -> {Redis, 数据库, 缓存}
    • 抽奖活动 :利用 SADD 报名,SRANDMEMBERSPOP 随机抽取中奖者。
    • 共同好友/共同关注 :利用 SINTER 计算两个用户的关注列表的交集。
    • 去重:记录已经处理过的消息 ID 或已经访问过的用户,防止重复处理。
5. ZSet / Sorted Set(有序集合)

在 Set 的基础上,为每个元素增加了一个 score(分数/权重),元素根据 score 进行排序。元素唯一,但 score 可以重复。底层是 跳表(SkipList)+ 哈希表。

  • 常用命令ZADD, ZREM, ZSCORE, ZRANGE, ZREVRANGE, ZRANGEBYSCORE
  • 使用场景
    • 排行榜 :游戏积分排行榜、商品销量榜、微博热搜榜(利用 score 存分数/热度,ZREVRANGE 获取 Top N)。
    • 延迟队列 :将 score 设置为任务执行的绝对时间戳 ,后台定时任务轮询 ZRANGEBYSCORE 0 当前时间戳,取出到期的任务执行。
    • 带权重的任务队列:VIP 用户的请求 score 更高,优先被消费。
    • 滑动窗口限流:利用 score 记录请求的时间戳,统计单位时间内的请求次数。

二、 高级/特殊数据结构

除了上述 5 种,Redis 还提供了一些针对特定场景优化的特殊数据结构:

6. Bitmap(位图)

本质上还是 String,但提供了针对位(bit) 操作的命令。由于 1 个字节有 8 个 bit,它在统计"是/否"状态时极其节省内存。

  • 常用命令SETBIT, GETBIT, BITCOUNT (统计 1 的个数), BITOP (位运算)
  • 使用场景
    • 用户签到Keyuser:sign:202607Offset 为日期(如 3 号),值为 1。一个月只需 31 bit(不到 4 个字节)。
    • 在线状态统计:记录用户 ID 是否在线,1 表示在线,0 表示离线。
    • 布隆过滤器 :利用多个哈希函数将数据映射到位图上,用于快速判断一个数据是否存在(存在不一定有,不存在一定没有),常用于缓存穿透防护。
7. HyperLogLog(基数统计)

用于统计海量数据中的不重复元素个数(基数) 。它的最大优势是极省内存 ,无论统计 100 个还是 10 亿个独立数据,都只需要固定 12KB 的内存。缺点是存在 0.81% 的标准误差,且不存储具体元素。

  • 常用命令PFADD, PFCOUNT, PFMERGE
  • 使用场景
    • 统计 UV(独立访客):统计网站或 APP 每天的独立访问用户数。
    • 统计在线人数:不需要知道具体是谁,只需要知道大概有多少人。
8. Geospatial(地理位置)

底层基于 ZSet 实现,用于存储和计算地理位置信息(经度、纬度)。

  • 常用命令GEOADD, GEODIST (计算距离), GEOPOS (获取坐标), GEOSEARCH (查找范围内的位置,注:Redis 6.2 后替代了 GEORADIUS)
  • 使用场景
    • LBS(基于位置的服务):查找"附近的人"、"附近的餐厅"、"附近的共享单车"。
    • 距离计算:计算打车软件中乘客与司机的距离。
9. Stream(消息流)

Redis 5.0 引入,专门为消息队列设计。支持多播、消费者组(Consumer Group)、消息确认(ACK),功能类似 Kafka。

  • 常用命令XADD, XREAD, XREADGROUP, XACK
  • 使用场景
    • 复杂的异步消息队列:当 List 做的简单队列无法满足"消息持久化、消费者组、消息确认机制、历史消息回溯"等需求时,使用 Stream。

三、 总结与选型指南

在实际开发中,如果你不知道用什么结构,可以参考以下"选型口诀":

  1. 存单个值/计数器/锁 ➡️ String
  2. 存对象/部分更新字段 ➡️ Hash
  3. 存列表/最新消息/简单队列 ➡️ List
  4. 存唯一集合/交并差集/抽奖 ➡️ Set
  5. 存排行榜/延迟队列/按权重排序 ➡️ ZSet
  6. 存签到/状态/0-1开关 ➡️ Bitmap
  7. 统计 UV/去重数量(允许微小误差) ➡️ HyperLogLog
  8. 存经纬度/附近的人 ➡️ Geospatial
  9. 需要完整的消息队列功能 ➡️ Stream

"缓存穿透"、"缓存击穿"和"缓存雪崩"是高并发场景下 Redis 缓存最容易出现的三大经典故障。它们的核心危害都是导致大量请求绕过缓存,直接打到数据库,最终可能压垮数据库

虽然名字相似,但它们的发生场景、原因和解决方案截然不同。以下是详细的深度解析:


一、 缓存穿透 (Cache Penetration)

1. 什么是缓存穿透?

  • 核心特征 :查询的数据在缓存和数据库中都不存在
  • 通俗比喻:你去找一个根本不存在的人,门卫(缓存)说没这人,你只能去屋里(数据库)找,屋里也没有。下次另一个人来找,门卫还是说没这人,又得去屋里找。
  • 发生场景
    • 恶意攻击 :黑客故意用大量不存在的 ID(如 id=-1)发起请求。
    • 业务代码 Bug:查询了错误的数据源。

2. 危害

每次请求都会穿透缓存,直接查询数据库。如果并发量大,数据库会瞬间承受巨大压力。

3. 解决方案

  • 方案 A:缓存空值/默认值(最常用)
    • 做法 :如果数据库也没查到数据,在 Redis 中缓存一个空值(如 ""null),并设置一个较短的过期时间(如 3~5 分钟)。
    • 优点:实现简单,能有效拦截重复的无效请求。
    • 缺点:如果攻击者每次使用不同的随机 ID,会导致缓存中充斥大量空值,浪费内存。
  • 方案 B:布隆过滤器 (Bloom Filter)(终极方案)
    • 做法 :在访问缓存之前,先让请求经过布隆过滤器。布隆过滤器能快速判断一个数据是否绝对不存在。如果布隆过滤器说"不存在",则直接拦截,不查缓存和数据库。
    • 优点 :极其节省内存,拦截率极高。Redis 4.0+ 提供了 RedisBloom 模块。
    • 缺点 :存在一定的误判率 (说存在,但实际可能不存在),且不能删除元素。通常结合"缓存空值"一起使用。

二、 缓存击穿 (Cache Breakdown)

1. 什么是缓存击穿?

  • 核心特征单个热点 Key 在过期的瞬间,恰逢大量并发请求同时访问该 Key。
  • 通俗比喻:一堵墙很坚固,但墙上有一个洞(热点 Key 过期),所有的水(高并发请求)都从这个洞里猛烈地喷进来,冲垮了后面的堤坝(数据库)。
  • 发生场景
    • 首页的"爆款商品"、"热搜新闻"等超高并发的数据,其缓存刚好到了过期时间。
    • 第一个请求发现缓存失效,去查数据库;此时后续成千上万个请求也发现缓存失效,全部涌向数据库。

2. 危害

瞬间的高并发请求直接打到数据库,导致数据库 CPU 飙升、连接池耗尽甚至宕机。

3. 解决方案

  • 方案 A:互斥锁(分布式锁 / 本地锁)
    • 做法 :在获取缓存失败时,尝试获取锁(如 Redis 的 SETNX 或 Java 的 synchronized)。只有获取到锁的线程才去查询数据库并重建缓存;其他没获取到锁的线程,要么休眠重试 ,要么直接返回默认值/错误
    • 优点:保证了数据的一致性,不会有多余的请求打到数据库。
    • 缺点:线程需要等待锁,性能有一定下降。
  • 方案 B:逻辑过期(不设置物理 TTL)
    • 做法 :Redis 中的 Key 不设置过期时间 (物理不过期),而是在 Value 中额外存储一个逻辑过期时间
    • 当线程发现逻辑过期时,不阻塞 ,直接返回旧数据给前端。同时,开启一个异步线程去查询数据库并更新缓存。
    • 优点:性能极高,没有线程阻塞,用户体验好。
    • 缺点 :在异步线程更新完成前,会返回短暂的旧数据(数据一致性要求不高的场景适用)。

三、 缓存雪崩 (Cache Avalanche)

1. 什么是缓存雪崩?

  • 核心特征大量 Key 在同一时间集体过期 ,或者 Redis 服务直接宕机
  • 通俗比喻:原本挡在前面的大坝(缓存)突然全面崩塌,所有的洪水(请求)瞬间倾泻到下游的村庄(数据库),造成毁灭性打击。
  • 发生场景
    • 开发人员在设置缓存过期时间时,使用了统一的值(如全都是 2 小时),导致 2 小时后缓存集体失效。
    • Redis 主节点宕机,且主从切换失败。

2. 危害

整个系统的请求全部打到数据库,导致数据库彻底崩溃,系统全面瘫痪。

3. 解决方案

  • 方案 A:针对"大量 Key 同时过期"
    • 做法 :在原有的过期时间基础上,加上一个随机值(如 1~5 分钟的随机数)。这样可以将集体过期的时间点打散,避免同一时刻发生雪崩。
  • 方案 B:针对"Redis 宕机"(高可用架构)
    • 做法 :不要使用单机 Redis。采用 主从复制 + 哨兵模式(Sentinel)Redis Cluster 集群模式,实现自动故障转移。同时开启 RDB 和 AOF 持久化,防止数据丢失。
  • 方案 C:兜底方案(限流、降级、多级缓存)
    • 服务限流:当发现数据库压力过大时,直接丢弃部分请求,保护核心业务(如使用 Sentinel 限流)。
    • 服务降级:返回兜底数据(如"系统繁忙,请稍后再试"或静态默认数据)。
    • 多级缓存 :引入本地缓存(如 Caffeine、Guava Cache)。请求先查本地缓存,再查 Redis,最后查数据库。即使 Redis 挂了,本地缓存还能扛住一部分压力。

四、 核心对比与总结

为了在面试或实战中快速区分,请记住以下核心差异:

故障类型 核心特征 数据是否存在 发生原因 解决核心思路
缓存穿透 此人 不存在(缓存和DB都没有) 查询根本不存在的数据 / 恶意攻击 拦截无效请求(布隆过滤器、缓存空值)
缓存击穿 单点突破 存在,但刚好过期 单个热点 Key 过期 + 高并发 控制并发重建(互斥锁、逻辑过期)
缓存雪崩 全面崩溃 存在,但集体过期 / Redis挂了 大量 Key 同时过期 / Redis 宕机 打散过期时间、高可用、限流降级

五、 面试实战加分项

如果面试官问:"你们项目中是怎么解决这些问题的?"

不要只背概念,要给出组合拳方案:

"在我们的电商项目中:

  1. 针对穿透 ,我们在商品详情页使用了布隆过滤器 ,拦截了 99% 的恶意不存在的 ID 请求;对于极少量的漏网之鱼,配合缓存空值(设置 5 分钟过期)来兜底。
  2. 针对击穿 ,对于首页的'秒杀商品'这种绝对热点,我们采用了逻辑过期 的方案,保证高并发下不阻塞线程;对于普通的商品详情,使用了 Redisson 分布式锁来防止并发重建缓存。
  3. 针对雪崩 ,我们在设置 TTL 时,统一加上了 1~5 分钟的随机值 ;同时 Redis 采用了 Cluster 集群 保证高可用;并且在网关层配置了 Sentinel 限流,确保即使极端情况下 Redis 出问题,数据库也不会被打垮。"
相关推荐
-To be number.wan1 小时前
数据库系统 | 数据库安全与完整性
数据库·学习
Omics Pro3 小时前
首个针对生物医药LLM智能体的全流程过程级评测框架
数据库·人工智能·windows·redis·量子计算
要开心吖ZSH3 小时前
MVCC 进阶:快照读 vs 当前读、幻读与 Next-Key Lock
java·数据库·sql·mysql·mvcc
水木流年追梦3 小时前
agent面试必备31- AI Agent 核心进阶:工具路由(Tool Routing)
数据库·人工智能·oracle·面试·职场和发展·embedding
栈溢出了3 小时前
Redis 消息队列笔记:List 与 Pub/Sub
redis·笔记·list
xcLeigh3 小时前
KES运维自动化与脚本体系实战
运维·数据库·自动化·脚本·数据迁移·kes
大气的小蜜蜂4 小时前
领域层的服务
java·前端·数据库
Devin~Y4 小时前
抖音级短视频推荐与直播带货平台面试实战:从 Java 微服务到 RAG 智能客服全链路解析
java·spring boot·redis·spring cloud·kafka·agent·rag
翔云1234564 小时前
简单概括主库上 Executed_Gtid_Set 是什么时候更新的
数据库·mysql