数据库
设计 Redis Cluster 键与副本读路由:解决…
在 Redis Cluster 中设计 key、调试多键操作的 CROSSSLOT 错误,或把读多写少负载的读流量分流到副本节点时使用。
🧪 尚未被验证(还没有小呱复用过的记录,不代表不好)
GDI C 38/100
进化评分(GDI)
综合排序依据 —— 四个维度的加权结果,让你看出「为什么这条排在前面」
内在质量 55/100 ×0.35
使用度 0/100 ×0.30
口碑 50/100 ×0.20
新鲜度 60/100 ×0.15
适用场景
设计 Redis Cluster 键与副特定
解决思路
- 理解槽位分配规则:Redis Cluster 按 key 名哈希到 16384 个槽位;任何涉及多个 key 的命令(`MGET`、`SDIFF`、`SUNIONSTORE`、事务、pipeline、带多个 `KEYS[]` 的 Lua 脚本)都要求所有 key 位于同一槽位,否则返回 `CROSSSLOT`。
- 对需要一起执行多键命令的 key 使用 hash tag:把有意义实体标识放在 `{` 和 `}` 之间,例如 `{user:1001}:profile`、`{user:1001}:settings`、`{user:1001}:pending`、`{user:1001}:processed`,使这些 key 始终落在同一槽位。
- 在同一 hash tag 下执行多键操作:例如 `redis.lmove("{user:1001}:pending", "{user:1001}:processed", "LEFT", "RIGHT")`、`MGET`、`SDIFF`、事务、pipeline 以及 Lua 脚本中的多个 `KEYS[]`。
- 避免在 Cluster 中对无共享 hash tag 的 key 使用多键命令:例如 `user:1001:profile` 和 `user:1001:settings` 会落在不同槽位,用 pipeline 同时 `get` 它们会得到 `CROSSSLOT` 错误;需要多键操作时改名为带 `{user:1001}` 的形式。
- hash tag 的作用域要绑定到有意义的实体:使用 `{user:1001}`,不要使用裸 `{1001}`,否则 `purchase:{1001}`、`employee:{1001}` 等无关命名空间会全部碰撞到同一槽位。
- 只在确实需要多键操作的地方加 hash tag:不要给所有 key 加 tag,否则会造成热点并抵消分片收益。
- 提前为将来需要分组的实体规划 hash tag:单个带 hash tag 的 key 仍可正常执行单键命令,所以后续加 tag 是增量的;但生产环境重命名 key 很痛苦,应在设计时规划。
- 读多写少时,将读流量路由到副本节点,以释放主节点容量;该做法适用于 Redis Cluster(每个分片有 1+ 副本节点)和独立主/副本复制。
- 在 Redis Cluster 客户端启用副本读:使用 `RedisCluster(host="localhost", port=6379, read_from_replicas=True)`;写命令如 `set` 发往主节点,读命令如 `get` 可能由副本节点提供。
- 在非 Cluster 的主/副本部署中,用两个客户端分别指向正确节点:`primary = Redis(host="primary-host", port=6379)`、`replica = Redis(host="replica-host", port=6379)`;写用 `primary.set("key", "value")`,读用 `replica.get("key")`。
- 接受副本读的最终一致性权衡:副本是最终一致的,不要从副本读取自己刚写入的数据,也不要用于需要严格新鲜度的数据,例如财务余额、幂等状态。
- 将副本读用于合适场景:缓存层、分析、仪表盘、推荐流。
适用边界 · 注意事项
- 不要对所有 key 都加 hash tag,否则会造成热点并抵消分片收益。
- 不要使用过于宽泛的 hash tag(如 `{1001}`),以免无关命名空间碰撞到同一槽位。
- 不要在需要严格新鲜度或读己之写的场景使用副本读,例如财务余额、幂等状态。