0
0
0

redis 模糊知识点整合版

文章摘要
|

1. String 和 Hash:不要按“单属性、多属性”简单区分

类型

存储方式

常见场景

String

字符串,也可以是序列化后的完整对象

缓存对象、计数器、分布式锁

Hash

一个 key 下存多个 field-value

对象属性存储、按字段读取和更新

你之前的模糊点: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 分桶”。

方案

key 中的桶号

field

特点

按区间分桶

id / 100

id % 100

非负整数 ID 下,每桶最多100个用户条目

固定100个桶

id % 100

完整 ID

桶数量固定,但每桶条目会持续增加

易错点:

  • 两种公式都可以,但不能混着讲。

  • 每桶限制100条,不代表一定没有大 key,还要看每条数据大小。

  • 除法、取余通常不是主要性能成本,网络往返、批量操作和跨桶查询更值得关注。

  • 普通 Hash 不是有序分页索引,不能直接当数据库分页使用。

背诵版:

已知用户 ID 后,通过固定公式算出桶号和 field,直接定位数据,无需扫描所有 key。分桶可以控制单个 Hash 的规模,但还要限制 value 大小,并考虑批量查询和分页成本。

3. 大 key、热 key、内存淘汰:三个概念

概念

含义

主要风险

大 key

单个 key 的 value 很大,或者集合元素很多

操作耗时、网络传输大、删除和迁移成本高

热 key

某个 key 的访问频率特别高

单节点负载集中

内存达到上限

Redis 触及 maxmemory 配置

按淘汰策略淘汰数据,或使需要分配内存的写操作失败

你之前的模糊点:“拒绝策略”表达不够准确,应说 内存淘汰策略;例如 noeviction 下,需要分配内存的写命令可能报错。

背诵版:

大 key 看数据规模,热 key 看访问频率,内存淘汰看实例的内存限制和配置策略。一个 key 可以同时是大 key 和热 key。

4. 缓存穿透、击穿、雪崩

问题

典型场景

常见措施

穿透

查询不存在的数据,缓存和数据库都没有

缓存空值、布隆过滤器、参数校验

击穿

一个热点 key 失效,大量请求同时回源

互斥重建、逻辑过期并返回旧值

雪崩

大量 key 集中失效,或 Redis 不可用

随机 TTL、限流、熔断、降级和高可用

你之前的模糊点:

  • “一大批 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. 主从切换丢锁,与持锁线程暂停,是两类故障

故障

发生过程

后果

主从切换丢锁

A 加锁成功,尚未复制主节点就宕机,新主没有锁

B 也能获取锁

持锁线程暂停

A 暂停导致无法续期,锁过期,B 获取锁,A 又恢复

A、B 可能同时执行

两种情况都可能导致多个客户端同时进入业务。

注意:进程真正退出后重启,不会从原来的代码位置继续;长时间 GC 暂停或机器挂起后恢复,才可能接着执行旧代码。

几种措施分别解决什么?

措施

解决的问题

能否单独保证关键业务正确

锁的过期时间

避免锁长期不释放

不能

看门狗续期

降低业务未完成时锁过期的概率

不能

Lua 安全解锁

避免误删别人的锁

不能

WAIT

降低异步复制尚未完成带来的风险

不能提供强一致保证

资源端原子校验

拒绝不符合条件的业务写入

取决于具体设计,可用于保证业务约束

背诵版:

锁过期不代表旧线程停止,主从异步复制也可能导致故障切换丢锁。看门狗和安全解锁各有作用,但关键业务仍需要数据库条件更新、唯一约束、幂等或隔离令牌兜底。

8. Fencing Token:让资源端拒绝旧请求

Fencing Token,中文可以叫隔离令牌。

步骤

事件

①

A 获取锁,拿到令牌 41,随后暂停

②

A 的锁过期,B 获取锁,拿到令牌 42

③

B 携带 42 写入,资源端记录 42

④

A 恢复后携带 41 写入,资源端拒绝

成立条件:

  • 令牌随锁授予严格递增,故障后不能回退或重复。

  • 数据库或下游资源必须实际校验。

  • 校验令牌和业务写入必须原子完成。

  • 令牌生成与锁授予要正确绑定,不能随意在抢锁后调用 INCR 就认为安全。

边界:资源端接受新令牌后,才能据此拒绝旧令牌;它不会因为 Redis 锁过期,就自动知道旧客户端失去了锁。

背诵版:

Fencing Token 为每次锁授予提供递增编号,资源端原子校验编号并执行操作,拒绝落后于已接受编号的请求,防止旧持锁者覆盖新结果。

9. Redisson:lock() 与 tryLock()

它们是 RLock 对象的方法,不是两种不同的锁。

RLock lock = redisson.getLock("order:lock:123");

方法

含义

lock.lock()

等待直到获取锁

lock.tryLock()

立即尝试,返回是否成功

lock.tryLock(3, TimeUnit.SECONDS)

最多等待3秒

lock.tryLock(3, 10, TimeUnit.SECONDS)

最多等待3秒,获得后租期10秒

续期要点:

  • 不指定租期时,通常使用看门狗自动续期。

  • 显式指定正的租期时,按固定租期过期,不使用看门狗自动续期。

  • 是否续期,不取决于方法叫 lock 还是 tryLock。

  • RLock 来自 Redisson 第三方库,Java 8 不自带,也不等于 Redlock 算法。

背诵版:

lock 和 tryLock 都是 Redisson RLock 的加锁方法,区别主要在等待方式和返回结果;是否使用看门狗,主要看是否显式指定正的租期。

10. Stream:未确认消息、接管与重复消费

前提是消费组模式,未开启 NOACK。

A 收到消息
  → 消息进入 PEL 待确认列表
  → A 未 ACK 就宕机
  → B 通过认领命令接管超时消息
  → B 处理成功后 ACK

命令

用途

XPENDING

查看待确认消息

XCLAIM

接管指定的待确认消息

XAUTOCLAIM

扫描并接管满足空闲时间条件的消息

XACK

从该消费组的 PEL 中移除确认记录,不等于删除 Stream 消息

你问到的核心风险:

B 接管消息,只改变 Redis 记录的归属,无法停止 A 已经开始执行的业务代码。A 恢复后仍可能继续执行。

因此,业务需要幂等:

开启数据库事务
  → 插入消费记录:业务事件 ID 有唯一约束
  → 执行业务更新
提交事务
  → XACK
  • 消费记录和业务更新必须在同一个事务中。

  • 业务成功、ACK 前宕机,再次投递时能够识别已处理。

  • 不能先查询“有没有消费过”,然后无约束地更新,仍然存在并发竞争。

  • 调用外部支付等接口时,还要使用下游支持的幂等号。

  • 接管阈值过短,会把正常慢处理误判成需要接管;空闲超时不等于消费者一定宕机。

背诵版:

Stream 未确认消息保留在 PEL,其他消费者可以通过认领机制恢复处理。但认领不能阻止旧消费者继续执行,因此需要通过业务唯一标识、唯一约束和事务实现幂等,业务提交成功后再 ACK。


最后集中背这六句

  1. 主节点之间是分片,主从节点之间是复制。

  2. 穿透查不存在,击穿是热点失效,雪崩是大面积缓存失效。

  3. 锁过期不等于旧线程停止,消息被接管也不等于旧消费者停止。

  4. 看门狗负责续期,Lua 负责安全解锁,都不能单独保证业务正确。

  5. 隔离令牌由资源端原子校验,业务幂等由唯一约束和事务等机制保证。

  6. 消息先处理成功,再确认;允许重复投递,防止重复产生业务效果。

支持与分享

如果这篇文章对你有帮助,欢迎分享给更多人或者给予支持!

评论