Redis不同场景下的注意事项
Redis常见的 使用场景:
- 缓存系统(核心场景)
- 存储热点数据,减少数据库访问压力。提升接口响应速度。
- 技术点:
- 用String/Hash 存储结构化数据
- 结合过期时间(TTL)和缓存淘汰策略(如LRU)管理内存。
- 解决缓存问题:穿透(布隆过滤器)、击穿(互斥锁)、雪崩(随机过期时间)。
- 计数器与限流
- 实时统计高频变动的值,或限制接口访问访问频率。
- 技术点:
- 用String存储单计数器(INCR )。
- 用HASH存储多维计数器(HINCRBY user:100 stats:likes 1)
- 限流可结合Expire自动重置技术(如 INCR limit:ip:192.168.1.1 + EXPIRE … 60)。
- 消息队列
- 实现简单的异步通信,解耦生产者和消费者。
- 技术点:
- 用List模拟队列:LPUSH(生产)+ RPOP(消费),或阻塞版本BRPOP避免空轮询。
- 缺点:无消息确认机制,需要结合确认机制,需结合AOF持久化减少丢失风险;高级需求(如延时队列)需要额外设计。
- 排行榜与实时排名
- 作用:根据分数动态排序,商品销量榜,用户贡献榜。
- 技术点:
- 用Sorted Set存储:ZADD rank:game user:100 95(用户 100 分数 95),ZRANGE rank:game 0 9 WITHSCORES 取前 10 名。
- 支持分数自增(ZINCRBY),适合动态更新排名。
- 分布式锁
- 作用:在分布式系统中保证临界资源的互斥访问(如库存扣减、订单创建)
- 优化:使用Redisson框架,支持自动续期、公平锁、可重入等高级特性。
- 用户行为分析
- 记录用户行为状态,快速统计群体特征。
- 技术点:
- 用BitMap存储每日登录状态:SETBIT login:20201001 user:100 1(用户100在20201001登录)通过BITOP作位运算统计活跃用户。
- 用SET存储用户标签:SADD tag:view:goods100 :100,通过交集(SINTER)找共同标签用户。
- 地理信息服务
- 作用:存储地理位置,实现附近搜索,距离计算等功能;
- 技术点:
- 用Geospatial命令:GEOADD shops 116.40 39.90 shop:1 存储经纬度,GEORADIUS shops 116.40 39.90 5 km 查找 5 公里内的商家。
- 会话存储
- 作用:存储用户登录状态(如Session、Token),支持分布式系统共享会话。
- 技术点:
- 用String存储Session/Token:SET session:user100 ”xxx“EX 7200(2小时过期)。
- 结合EXPIRE自动销毁过期会话,减轻服务器存储压力。
缓存场景
缓存关键问题:数据一致性、可用性、内存效率。同时规避缓存常见风险(如穿透、击穿、雪崩)。
-
缓存穿透:避免”不存在的数据“,穿透到数据库。
问题本质:请求查询 不存在的数据,导致缓存无法命中。所有请求都直接打到数据库,导致数据库压力骤增。
解决方案:- 空值缓存:数据库给查询为空时,可以将空值存入缓存。并设置较短的过期时间。避免重复穿透。 恶意攻击会导致内存膨胀。
- 布隆过滤器:在缓存前加一层布隆过滤器,将数据库中存在的key提前存入过滤器,请求先过过滤器。
- 参数校验:接口层直接拦截非法参数(如id<0,非预期格式的key),从源头阻断无效请求。
-
缓存击穿:避免热点key过期,导致数据库瞬间过载。
- 问题本质:某个高频访问的热点key过期瞬间大量请求同时命中失效的缓存,全部穿透到数据库,造成数据库“瞬间峰值”。
- 解决方式:
- 互斥锁(分布式锁):缓存未命中时,只有一个请求能获取锁并查询数据库,其他请求自选等待(或直接返回默认值),避免“并发穿透。注意:锁过期时间需要大于数据库查询时间,防止锁提前释放导致并发问题;同时注意避免死锁。
- 热点key永不过期
- 业务层不设置TTL,通过后台定时任务(如Cron脚本主动更新(如每小时更新商品信息)。
- 若数据需要实时更新,壳子啊数据变更时主动SET命令覆盖缓存,而非TTL过期。
- 过期时间错开:对同类热点key设置随机过期时间(如基础过期时间+5~8分钟随机值)。
-
缓存雪崩问题,避免大量key集体过期/集群宕机导致数据库崩溃
问题本质:
同一时间段内大量缓存key集中过期,请求全部穿透到数据库。
Redis缓存集群整体宕机,缓存层整体失效。所有请求直接打向数据库。
解决方法:- 针对大量key同时过期:给每个key设置随机过期时间。分散过期时间。
- 针对集群宕机:
- 缓存使用集群高可用
- 多级缓存:在Redis之前使用本地缓存(如:Caffeine、GuavaCache),即使Redis宕机本地缓存也可以暂代部分请求,减轻数据库压力。
- 熔断降级:通过监控工具(如Prometheus+Grafana)实时检测数据库压力。当压力超过指定阈值时,出发熔断(直接返回默认值或错误提示),避免数据库崩溃。
- 流量控制:结合网关做接口限流,限制单位时间内的请求量。
数据一致性
缓存与数据库 尽量保持一致,避免缓存存旧数据,数据库存新数据 的矛盾场景。
- 选择合适的 更新策略:
- 处理缓存更新失败问题:
- 若数据库更新成功,但删除缓存失败,会导致缓存残留旧数据。解决方案:
- 重试机制:删除缓存失败后,通过 本地重试机制或消息队列 异步重试(如将”删除缓存任务存入队列中,失败后重新消费),确保缓存最终被删除。
- 版本号+时间戳:缓存中存储“数据+版本号”,查询时对比数据库版本号,若缓存版本低则主动更新。
- 若数据库更新成功,但删除缓存失败,会导致缓存残留旧数据。解决方案:
相关优化
Redis是内存数据库,需合理管理内存,避免内存溢出 性能下降。
- 缓存key的设计:
- 简洁且唯一:用“业务前缀+唯一标识”格式(如user:session:1001、goods:detail:abc123)避免key冲突
- 避免过长:key过长会增加内存占用。
- 避免大量小key,大量零散的小key会浪费内存(如 user💯name、user💯age)建议使用Hash合并(如 user:100 {name: “xxx”, age: 20})。
- 合理配置淘汰策略:当Redis内存到达maxmemory阈值时,需通过淘汰策略释放内存,避免OOM内存溢出。需根据业务选择策略:
- 核心策略推荐:
- LRU:淘汰 最近最少使用的key(适合热点数据场景,如商品详情),对应配置 maxmemory-policy allkeys-lru(所有 key 中选 LRU)或 volatile-lru(只在设置了 TTL 的 key 中选 LRU)。
- LFU:淘汰最近访问频率最低的key(适合长期低频访问数据,如历史订单)。对应配置:allkey-lfu或volatile-lfu。
- 避免使用的策略:
- noeviction(默认):内存满时,直接拒绝写请求,会导致业务报错,除非是只读场景。
- random:随即淘汰,可能淘汰热点数据,不推荐。
- 核心策略推荐:
- 控制缓存粒度与过期时间。
- 缓存粒度不宜过粗过细:
- 过粗:导致缓存数据冗余,更新世需全量替换,浪费带宽和内存。
- 过细:将对象每个属性单独存储,导致 key数量增加,和查询次数增多。
- 建议:按“业务查询维度”设计粒度,缓存对象采用聚合数据。
- 过期时间合适
- 缓存粒度不宜过粗过细:
监控与运维
- 核心指标:需要实时监控Redis的缓存命中率(目标>=95%),内存使用率、key过期数量、数据库访问量等。当指标异常时(如命中率骤降、内存超阈值) 及时告警。
- 工具推荐:Prometheus+Grafana、Redis Insight。
- 持久化配置:若缓存数据需要重启后恢复(如热点商品缓存),需要开启AOF和RDB持久化‘
- 优先使用AOF+RDB混合持久化。AOF保证数据不丢失(每秒刷盘),RDB用于数据快速恢复。
- 若缓存数据可重建(如数据库重新加载),可关闭持久化,减少IO操作。