【关于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 存一大段 JSONHash 的优势在于可以只修改或获取对象中的某个字段且更省内存。常用命令HSET,HGET,HMSET,HMGET,HDEL,HINCRBY使用场景对象存储存储用户信息User ID - {name, age, email}、商品信息。购物车Key为用户 IDField为商品 IDValue为商品数量。方便单独修改某个商品的数量。用户画像/属性存储用户的各种标签和属性值。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报名SRANDMEMBER或SPOP随机抽取中奖者。共同好友/共同关注利用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(位运算)使用场景用户签到Key为user: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。三、 总结与选型指南在实际开发中如果你不知道用什么结构可以参考以下“选型口诀”存单个值/计数器/锁➡️String存对象/部分更新字段➡️Hash存列表/最新消息/简单队列➡️List存唯一集合/交并差集/抽奖➡️Set存排行榜/延迟队列/按权重排序➡️ZSet存签到/状态/0-1开关➡️Bitmap统计 UV/去重数量允许微小误差➡️HyperLogLog存经纬度/附近的人➡️Geospatial需要完整的消息队列功能➡️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 宕机打散过期时间、高可用、限流降级五、 面试实战加分项如果面试官问“你们项目中是怎么解决这些问题的”不要只背概念要给出组合拳方案“在我们的电商项目中针对穿透我们在商品详情页使用了布隆过滤器拦截了 99% 的恶意不存在的 ID 请求对于极少量的漏网之鱼配合缓存空值设置 5 分钟过期来兜底。针对击穿对于首页的‘秒杀商品’这种绝对热点我们采用了逻辑过期的方案保证高并发下不阻塞线程对于普通的商品详情使用了Redisson 分布式锁来防止并发重建缓存。针对雪崩我们在设置 TTL 时统一加上了1~5 分钟的随机值同时 Redis 采用了Cluster 集群保证高可用并且在网关层配置了Sentinel 限流确保即使极端情况下 Redis 出问题数据库也不会被打垮。”