![[Pasted image 20251231141326.png]]
您的提问非常专业且切中要害。您的观点是完全正确的。
在 Redis Cluster(集群模式)下,确实存在 Key 必须落在同一个 Slot(哈希槽)才能在同一个 Lua 脚本中执行 的限制。如果不处理这个问题,V3 策略中的“无锁化 Lua 脚本”确实会失效。
以下是针对这个问题的详细分析、解决方案以及潜在风险:
1. 问题根源:Redis Cluster 的分片机制
Redis Cluster 将数据划分为 16384 个 Slot。默认情况下,Redis 使用 CRC16 算法对 Key 进行哈希计算 CRC16(key) % 16384 来决定该 Key 存储在哪个节点上。
- 如果
Key A算出在 Node 1。 - 如果
Key B算出在 Node 2。 - 当 Lua 脚本试图同时操作
Key A和Key B时,Redis 无法在一次请求中跨节点协调这两个 Key,会抛出CROSSSLOT Keys in request don't hash to the same slot错误。
2. 解决方案:Hash Tag(哈希标签)
要解决这个问题,必须强制所有参与 Lua 脚本计算的 Key 落在同一个 Slot 中。Redis 提供了 Hash Tag 机制。
原理: 如果 Key 中包含 {},Redis 只会计算 {} 内部字符串的哈希值来定位 Slot。
改造您的 Key 设计: 您简历中提到的 Key 设计如下:
d_mai_program_ticket_remain_number_resolution_节目id_票档idd_mai_program_seat_no_sold_resolution_hash_节目id_票档id
为了保证针对同一个节目的操作都在同一个节点,您需要在 Key 中加入 {}。
方案 A:以“节目ID”为锚点(粗粒度,推荐用于涉及全节目的校验)
将 Key 修改为:
- 余票:
d_mai_program_ticket_remain_{节目id}_票档id - 座位:
d_mai_program_seat_{节目id}_票档id
这样,无论票档 ID 是多少,只要 节目 ID 相同,CRC16(节目id) 的结果就相同,所有关于该节目的 Key 都会存储在同一个 Redis 节点上。
方案 B:以“节目ID + 票档ID”为锚点(细粒度,推荐用于仅涉及单票档的购买)
如果您的 Lua 脚本逻辑完全独立于其他票档(即购买一等票不需要检查二等票的状态),则可以将 Hash Tag 设为 {节目id_票档id}。
- Key:
d_mai_program_{节目id_票档id}_remain
这通常更优,因为它能把同一个热门节目的不同票档分散到不同节点,减轻单点压力。但前提是业务逻辑不需要跨票档(例如不需要校验“该用户在该节目下总共只能买2张,不限票档”)。
3. V3 策略的完善(面试应答话术)
在面试中,当面试官问到这个问题,或者您主动介绍 V3 策略时,可以这样补充:
“您说得非常对。在设计 V3 无锁化策略时,我也考虑到了 Redis Cluster 的 CROSSSLOT 问题。
为了确保 Lua 脚本能原子性地操作‘库存 Key’和‘座位 Key’,我使用了 Redis 的 Hash Tag 机制。
我将所有相关的 Key 进行了改造,例如在 Key 中嵌入
{ProgramID}。这样 Redis 在计算哈希槽时,只会根据{}内的 ProgramID 进行计算,从而强制同一个节目的所有相关数据(库存、座位表)都落到同一个 Redis 节点上。这样既利用了集群的容量优势,又保证了单次购票请求在单个节点内的原子性执行,避免了分布式锁的开销。”
4. 引入的新风险:数据倾斜(Data Skew)与热点 Key
使用了 Hash Tag 后,必须面对一个新的挑战:热点问题(Hot Key)。
- 现象:如果有一个超级明星的演唱会(热门 ProgramID),由于使用了
{ProgramID}作为 Hash Tag,该演唱会所有的库存、座位读写请求都会打向同一个 Redis 节点。 - 后果:该特定节点的 CPU 和网络可能被打满,而集群中其他节点却很空闲。
应对思路(进阶回答):
- 细化 Hash Tag 粒度:尽量按
{节目ID_票档ID}分片。这样一等票在 Node A,二等票在 Node B,分担了压力。 - 本地缓存(Caffeine):正如您简历中提到的,利用本地缓存挡住大量的“读”请求(如查询余票、查询座位图),只有真正的“写”请求(下单)才打到 Redis。
- 业务分片(大招):如果是极度热门的秒杀(如周杰伦演唱会),甚至可以将同一个票档的库存拆分成多份(Key_1, Key_2...),但这会极大增加 Lua 脚本和业务的复杂度,通常对于常规高并发售票,配合本地缓存和限流已经足够。
总结
您的 V3 策略是可行的,但必须配合 Hash Tag 设计才能在 Redis Cluster 中落地。指出这一点不仅不会减分,反而证明您对 Redis 底层原理(集群分片、CRC16 算法)有深刻的理解。