Redis 集群小坑
Redis 集群的核心机制:哈希槽
Redis 集群采用了 哈希槽(Hash Slots) 来管理键的分布。集群中总共有 16384 个哈希槽,每个键根据其名称通过哈希算法计算出一个槽号,并被分配到某个节点上进行存储。
- 当你执行类似
SET
或GET
这样的命令时,Redis 根据键的名称通过一致性哈希计算出该键应该属于哪个槽(slot),然后将该键存储在负责这个槽的节点上。 - 每个节点负责管理若干个哈希槽,因此每个节点只存储一部分数据。通过这种方式,Redis 集群能将数据分布在多个节点上,从而实现水平扩展。
为什么不能通过前缀直接删除?
1. 键分布在不同节点上:
- 在 Redis 集群中,键并不全部存储在一个节点上,而是根据哈希槽机制分布在多个节点上。每个节点只负责自己哈希槽范围内的键。
- 假设你有一个前缀
"user:"
,并且要删除所有以"user:"
开头的键。在 Redis 集群中,键user:1
可能分布在节点 A 上,而user:2
可能分布在节点 B 上,user:3
可能又分布在节点 C 上,依此类推。
2. 没有全局视图:
- Redis 集群的设计并没有提供全局的键列表视图。在集群中,
SCAN
命令或DEL
命令是限定在单个节点上的,不会自动跨节点操作。因此,无法在所有节点上同时查询或删除带有特定前缀的所有键。 - 当你尝试通过前缀删除时,Redis 集群的命令并不会跨越多个节点来查找和删除带有前缀的所有键。每个节点仅能操作自己存储的键,并且它不知道其他节点上的数据。
3. SCAN
命令限制:
- Redis 的
SCAN
命令虽然支持增量迭代来遍历当前节点中的所有键,但它并不会自动扫描整个集群中的所有键。SCAN
仅对当前节点有效,如果你要删除一个带前缀的键,必须逐个节点地扫描。 - 在集群模式下,
SCAN
命令并不会在所有节点上并行执行,它只会在某个特定节点上运行。这意味着,即使你使用了带有前缀匹配的SCAN
,你仍然需要手动连接和扫描每个节点上的数据,并找出符合条件的键。
举个例子
假设你有以下三个 Redis 集群节点,数据分别如下:
- 节点 A:负责哈希槽 0-5000,存储键
user:1
,user:2
- 节点 B:负责哈希槽 5001-10000,存储键
user:3
,user:4
- 节点 C:负责哈希槽 10001-16383,存储键
user:5
,user:6
当你尝试通过前缀 "user:"
删除键时,Redis 集群无法一次性删除所有节点上的这些键。你需要:
- 在节点 A 执行
SCAN
查找匹配前缀的键user:1
,user:2
。 - 在节点 B 执行
SCAN
查找匹配前缀的键user:3
,user:4
。 - 在节点 C 执行
SCAN
查找匹配前缀的键user:5
,user:6
。
每个节点上的数据必须单独处理,这就导致了无法像在单节点模式下那样直接使用前缀一次性删除所有匹配的键。
解决方案
为了在 Redis 集群中删除具有特定前缀的所有键,通常采用以下方法:
- 手动遍历每个节点:
- 使用
SCAN
命令遍历每个节点上的键,并检查它们是否匹配给定的前缀。匹配的键将被删除。这个过程需要你手动访问每个节点。
- 使用
- 分布式删除工具:
- 使用一些 Redis 集群管理工具(如 Redisson)或其他第三方库,来跨节点扫描键并执行删除操作。
- 定期清理机制:
- 设定 TTL(过期时间)等策略,减少不需要的键的存活时间,从而减轻批量删除的需求。
- 批量删除操作:
- 分批次删除键,并在每次删除时确保遍历每个节点上的数据。可以结合异步或并行处理技术,优化性能。
- (推荐)key中直接加大括号:
- 通过 {} 来指定 Redis 路由
例如:
- 通过 {} 来指定 Redis 路由
SET user:{1001}:name "John Doe"
SET user:{1001}:email "john.doe@example.com"
SET user:{1002}:name "Jane Smith"
user:{1001}:name 和 user:{1001}:email 会被路由到同一个节点。
user:{1002}:name 会被路由到另一个节点,和 user:{1001}:name 处于不同的哈希槽。
发表回复