Redis 应用场景及常见问题
Redis 应用场景及常见问题
penjc1. 引言
Redis(Remote Dictionary Server)是一种高性能的 NoSQL 数据库,主要基于内存存储,提供了丰富的数据结构,适用于多种应用场景,如缓存、分布式锁、计数器、消息队列等。
本文将详细介绍 Redis 的核心应用场景,并结合代码示例进行说明。
2. Redis 典型应用场景
2.1 缓存(Cache)
应用场景:
- 热点数据缓存:如商品详情、用户信息、文章内容等。
- 降低数据库压力:减少数据库 I/O,提高查询速度。
- 提升系统吞吐量:通过缓存减少数据库的直接访问次数。
代码示例:
1 | // 存储数据到 Redis 缓存 |
优化方案:
- LRU 过期策略 适用于内存受限场景。
- 逻辑过期 结合后台更新,防止缓存击穿。
2.2 分布式锁(Distributed Lock)
应用场景:
- 解决 并发 资源竞争问题,如商品秒杀、库存扣减。
- 适用于 多服务 共同访问同一资源的场景。
代码示例:
1 | RLock lock = redissonClient.getLock("lock:reduce:store"); |
优化方案:
- 使用 Redisson 提供的
RLock
,支持 可重入锁、看门狗机制 续期。 - 避免 死锁,释放锁前检查
isHeldByCurrentThread()
。
2.3 计数器(Counter)
应用场景:
- 统计 网站 PV/UV
- 统计 点赞数、访问量
- 限流 控制访问频率
代码示例:
1 | // 用户访问统计 |
优化方案:
- 定期落盘 到 MySQL,避免数据丢失。
- HyperLogLog 统计 UV(去重效果更好)。
2.4 全局唯一 ID(Distributed ID)
应用场景:
- 订单号生成
- 支付流水号
- 分布式系统唯一标识
代码示例:
1 | Long orderNum = stringRedisTemplate.opsForHash().increment("redis:only:number", "order", 1); |
优化方案:
- 避免 UUID,UUID 不可读、不可排序。
- 使用雪花算法(Snowflake) 生成更稳定的分布式 ID。
2.5 排行榜(Leaderboard)
应用场景:
- 游戏排行榜
- 电商销量排名
- 社交点赞/活跃度排名
代码示例:
1 | // 增加排行榜数据 |
优化方案:
- 使用 ZSet(跳表 SkipList) 存储排名。
- 定期归档 历史数据,防止数据过大影响性能。
2.6 限流(Rate Limiting)
应用场景:
- API 限流(防止恶意请求)
- 秒杀防刷
- 爬虫控制
代码示例:
1 | for (int i = 0; i < 112; i++) { |
Lua 脚本:
1 | local key = KEYS[1] |
优化方案:
- 使用 Lua 脚本 保证原子性。
- 滑动窗口限流,平滑限流效果。
3. Redis 常见问题
Redis 缓存穿透
缓存穿透定义
缓存穿透指的是请求一个不存在的数据,导致直接查询数据库,并且频繁查询数据库,造成缓存和数据库的双重压力。
解决方案
方案一:缓存空结果
对于不存在的数据,将空结果缓存一段时间,减少数据库查询频率。
- 优点:实现简单。
- 缺点:会缓存无效数据,占用Redis内存,可能导致缓存和数据库不一致。
方案二:布隆过滤器
布隆过滤器用于判断某个数据是否存在于集合中,适用于缓存穿透问题。布隆过滤器通过多个哈希函数和二进制数组实现高效判断。
- 优点:不会缓存无效数据。
- 缺点:实现较复杂,可能存在误判。
布隆过滤器工作原理
- 添加元素时,使用多个哈希函数对元素进行哈希,计算出多个位置,并将这些位置的值设置为1。
- 查询元素时,计算元素对应的多个位置,若所有位置均为1,则认为元素存在(可能存在误判);若有任何位置为0,则认为元素一定不存在。
布隆过滤器实现方式
- Guava
- Hutool
- Redisson
- 手写实现
缓存击穿
缓存击穿定义
缓存击穿是指当热点数据缓存失效或者没有缓存时,所有请求都会访问数据库,导致数据库负载过高。
解决方案
方案一:全局锁
在访问数据库之前,先请求全局锁,获得锁的线程才有资格访问数据库。由于分布式系统中本地锁无法控制其他服务的线程,所以要使用分布式锁。
方案二:设置热点数据永不过期
通过不设置过期时间来确保热点数据不会被删除。实现方式:
- 方式一:Redis不设置热点数据的过期时间(物理不过期)。
- 方式二:将过期时间设置在key的value中,发现过期后通过异步线程更新缓存(逻辑过期)。
缓存雪崩
缓存雪崩定义
缓存雪崩是指在某一时刻,大量的缓存数据同时过期,导致瞬间请求都落入数据库,进而压垮数据库。
缓存雪崩的解决方案
- Redis高可用:通过配置Redis Sentinel或Redis Cluster,避免Redis单点故障。
- 设置不同的过期时间:通过给不同的缓存key设置不同的过期时间,避免大量数据同时过期。
- 本地缓存+限流和降级:在Redis不可用的情况下,通过本地缓存和限流措施保护数据库。
Redis的内存使用策略
Redis内存策略
Redis提供了多种内存淘汰策略,确保内存使用的最大限度。策略包括:
- volatile-lru:使用LRU算法清除最近最少使用的键,仅对设置了过期时间的键有效。
- allkeys-lru:对所有键使用LRU算法。
- volatile-lfu:使用LFU算法清除最不常用的键,仅对设置了过期时间的键有效。
- allkeys-lfu:对所有键使用LFU算法。
- volatile-random:随机淘汰过期键。
- allkeys-random:对所有键随机淘汰。
- volatile-ttl:根据TTL(过期时间)淘汰。
- noeviction:不淘汰任何键,返回错误。
Redis集群能部署多少个主节点
从 Redis 3.0 版本开始,Redis支持集群模式,通过槽位(slot)来实现数据分片。
Redis集群支持最多16384个槽位,每个槽位对应一个主节点。所以最多支持16384个主节点。
Redis与数据库一致性
如何保证数据库与Redis的一致性?
当数据库进行增删改操作时,需要确保Redis缓存中的数据与数据库中的数据保持一致,避免数据不一致的问题。
- 缓存更新策略:当数据库更新时,需要主动更新Redis中的缓存数据。
- 双写一致性:在高并发环境下,可以通过分布式事务、消息队列等机制来保证数据库与缓存的一致性。
- 缓存失效机制:设置合理的缓存过期时间,定期从数据库同步数据到缓存中,避免因缓存过期导致的一致性问题。
** 延迟双删策略 **