redis 模糊知识点整合版
1. String 和 Hash:不要按“单属性、多属性”简单区分
你之前的模糊点:String 不只适合单一属性,也可以存完整用户对象的 JSON。
背诵版:
String 可以存普通字符串,也可以存序列化后的对象;Hash 适合按属性存储对象,方便单独读取和更新字段。具体选型要看访问方式,不能只看属性数量。
2. Hash 存对象与 Hash 分桶:是两种设计
一用户一个 Hash:
key:user:123
field:name value:张三
field:age value:25多个用户共用一个 Hash:
key:user:bucket:1
field:123 value:用户123的完整JSON
field:156 value:用户156的完整JSON第二种才涉及你说的“用户 ID 分桶”。
易错点:
两种公式都可以,但不能混着讲。
每桶限制100条,不代表一定没有大 key,还要看每条数据大小。
除法、取余通常不是主要性能成本,网络往返、批量操作和跨桶查询更值得关注。
普通 Hash 不是有序分页索引,不能直接当数据库分页使用。
背诵版:
已知用户 ID 后,通过固定公式算出桶号和 field,直接定位数据,无需扫描所有 key。分桶可以控制单个 Hash 的规模,但还要限制 value 大小,并考虑批量查询和分页成本。
3. 大 key、热 key、内存淘汰:三个概念
你之前的模糊点:“拒绝策略”表达不够准确,应说 内存淘汰策略;例如 noeviction 下,需要分配内存的写命令可能报错。
背诵版:
大 key 看数据规模,热 key 看访问频率,内存淘汰看实例的内存限制和配置策略。一个 key 可以同时是大 key 和热 key。
4. 缓存穿透、击穿、雪崩
你之前的模糊点:
“一大批 key 同时过期”属于雪崩。
随机 TTL 主要缓解集中失效,不能解决 Redis 宕机。
缓存没命中不等于穿透,正常缓存失效也会未命中。
背诵版:
穿透是查不存在的数据,击穿是热点 key 失效,雪崩是大量缓存同时失效或 Redis 不可用。分别通过空值缓存或布隆过滤器、互斥重建,以及随机过期和限流降级来缓解。
5. 互斥重建缓存:流程要完整
查询缓存
→ 未命中,尝试获取重建锁
→ 获取成功,再次查询缓存
→ 仍未命中,查询数据库
→ 写回缓存
→ 安全释放锁没有获取到锁的请求,进行有限重试,或在业务允许时返回旧值,不能无限等待。
关键细节:
SET lockKey 唯一标识 NX PX 超时时间:原子地加锁并设置过期时间。双查缓存:前一个请求可能已经完成重建。
Lua 校验标识后删除:避免误删别人的锁。
查完数据库后是写回缓存,不是重新写数据库。
背诵版:
缓存未命中后抢互斥锁,成功后再次检查缓存,确认仍不存在才回源并写入缓存。锁需要过期时间和唯一标识,释放时原子校验并删除,其他请求进行有限重试或返回旧值。
6. Redis Cluster:分片与复制
同一个 key 正常只写入负责该槽的一个 Master,不会广播到所有 Master。
key → 哈希槽 → 负责该槽的 Master → 该 Master 的 Replica集群共有 16384 个哈希槽。
不同客户端访问相同 key,按照槽映射定位到相应节点。
主节点之间分片,各负责部分数据。
主从之间异步复制,保存同一分片的副本。
背诵版:
Redis Cluster 的主节点之间是分片,主从之间是复制。一个 key 根据哈希槽定位到对应主节点,再由该主节点复制给自己的从节点,与请求来自哪个客户端无关。
7. 主从切换丢锁,与持锁线程暂停,是两类故障
两种情况都可能导致多个客户端同时进入业务。
注意:进程真正退出后重启,不会从原来的代码位置继续;长时间 GC 暂停或机器挂起后恢复,才可能接着执行旧代码。
几种措施分别解决什么?
背诵版:
锁过期不代表旧线程停止,主从异步复制也可能导致故障切换丢锁。看门狗和安全解锁各有作用,但关键业务仍需要数据库条件更新、唯一约束、幂等或隔离令牌兜底。
8. Fencing Token:让资源端拒绝旧请求
Fencing Token,中文可以叫隔离令牌。
成立条件:
令牌随锁授予严格递增,故障后不能回退或重复。
数据库或下游资源必须实际校验。
校验令牌和业务写入必须原子完成。
令牌生成与锁授予要正确绑定,不能随意在抢锁后调用
INCR就认为安全。
边界:资源端接受新令牌后,才能据此拒绝旧令牌;它不会因为 Redis 锁过期,就自动知道旧客户端失去了锁。
背诵版:
Fencing Token 为每次锁授予提供递增编号,资源端原子校验编号并执行操作,拒绝落后于已接受编号的请求,防止旧持锁者覆盖新结果。
9. Redisson:lock() 与 tryLock()
它们是 RLock 对象的方法,不是两种不同的锁。
RLock lock = redisson.getLock("order:lock:123");续期要点:
不指定租期时,通常使用看门狗自动续期。
显式指定正的租期时,按固定租期过期,不使用看门狗自动续期。
是否续期,不取决于方法叫
lock还是tryLock。RLock来自 Redisson 第三方库,Java 8 不自带,也不等于 Redlock 算法。
背诵版:
lock 和 tryLock 都是 Redisson RLock 的加锁方法,区别主要在等待方式和返回结果;是否使用看门狗,主要看是否显式指定正的租期。
10. Stream:未确认消息、接管与重复消费
前提是消费组模式,未开启 NOACK。
A 收到消息
→ 消息进入 PEL 待确认列表
→ A 未 ACK 就宕机
→ B 通过认领命令接管超时消息
→ B 处理成功后 ACK你问到的核心风险:
B 接管消息,只改变 Redis 记录的归属,无法停止 A 已经开始执行的业务代码。A 恢复后仍可能继续执行。
因此,业务需要幂等:
开启数据库事务
→ 插入消费记录:业务事件 ID 有唯一约束
→ 执行业务更新
提交事务
→ XACK消费记录和业务更新必须在同一个事务中。
业务成功、ACK 前宕机,再次投递时能够识别已处理。
不能先查询“有没有消费过”,然后无约束地更新,仍然存在并发竞争。
调用外部支付等接口时,还要使用下游支持的幂等号。
接管阈值过短,会把正常慢处理误判成需要接管;空闲超时不等于消费者一定宕机。
背诵版:
Stream 未确认消息保留在 PEL,其他消费者可以通过认领机制恢复处理。但认领不能阻止旧消费者继续执行,因此需要通过业务唯一标识、唯一约束和事务实现幂等,业务提交成功后再 ACK。
最后集中背这六句
主节点之间是分片,主从节点之间是复制。
穿透查不存在,击穿是热点失效,雪崩是大面积缓存失效。
锁过期不等于旧线程停止,消息被接管也不等于旧消费者停止。
看门狗负责续期,Lua 负责安全解锁,都不能单独保证业务正确。
隔离令牌由资源端原子校验,业务幂等由唯一约束和事务等机制保证。
消息先处理成功,再确认;允许重复投递,防止重复产生业务效果。
