UNLINK 是什么?
官方文档:https://redis.io/docs/latest/commands/unlink/
UNLINK 是 Redis 4.0 引入的异步删除 命令,语法与 DEL 一致:UNLINK key [key ...]
工作原理
- 主线程:仅将 key 从 keyspace(字典)中摘除,时间复杂度 O(1),立即返回。
- 后台 I/O 线程:异步释放实际内存。
这意味着 UNLINK 之后该 key 对其他客户端立刻不可见,但内存是延迟回收的。
核心差异:
| 对比 | DEL | UNLINK |
|---|---|---|
| 删除方式 | 同步,主线程阻塞 | 异步,主线程仅解链接 |
| 内存回收 | 立即释放 | 后台 BIO 线程异步释放 |
| 大 Key 影响 | 严重阻塞 | 几乎无阻塞 |
| 返回值 | 被删除 Key 数量 | 被解链接 Key 数量 |
适用场景
实践上可以将 UNLINK 作为 DEL 的默认替代品 ,几乎没有副作用,唯一注意点是内存不会立即释放------如果你的逻辑依赖"删完立刻能看到内存下降"(如监控告警),需要考虑这个延迟。
1. 删除大 key(最核心场景)
大 String(几十 MB)、大 Hash/Set/ZSet/List(几十万成员)用 DEL 会阻塞主线程几十甚至几百毫秒,严重影响其他请求。此时必须用 UNLINK。
大 Key 删除优先 UNLINK------集合类型(Hash/List/Set/Sorted Set)尤其重要
2. 批量删除大量 key
bash
UNLINK key1 key2 key3 ... key10000
避免一次性释放大量内存导致主线程卡顿。
3. 高并发、低延迟敏感的服务
对 P99/P999 延迟要求极高的场景,即使 key 不大,也建议统一用 UNLINK 替代 DEL,消除毛刺风险。
Redis Set / ZSet 存储上限
Redis 不对 collection 做数量限制,只受内存约束
生产环境必须为每个业务 key 设定 member 数量上限,否则:
- 单个大 ZSET 的 ZADD/ZREM 时间复杂度 O(log N),N 过大时延迟飙升
- DEL 一个百万级 ZSET 会阻塞 Redis 毫秒到秒级
- RDB 持久化时大 key 导致 fork 耗时、AOF rewrite 卡顿
- 主从同步全量同步时大 key 占满带宽
SET 大小建议:
- 单 Value 控制在 10 KB 以内------这是 Redis 官方推荐的安全阈值
- 超过 100 KB 需要评估------考虑压缩或拆分
- 超过 1 MB 必须优化------使用分片存储或更换数据结构
ZRemRangeByRank
Set 最大的坑是 SADD 无限增长却无感知,Set 没有排序,无法区分"哪些是旧的可以删",所以你没办法用一个简单命令裁掉多余部分。
Set 没有内置的容量上限机制------你可以查大小,但 Redis 不会主动阻止它增长,也没有原生命令能在写入时顺手淘汰旧数据。容量控制必须完全依赖业务代码自己去做,而且做起来很别扭
- 想限制上限,得先
SCARD判断,超了再手动SREM------但 Set 无序,你不知道该删哪个 - 没有"删最旧的一个"这种语义,只能
SRANDMEMBER随机删,或者业务层自己维护一个删除队列
问题的本质不是"看不见大小",而是Set 缺乏淘汰语义 ,无法表达"按某种顺序淘汰最旧/最低优先级的元素"。而 ZSet 用 score 引入了排序,ZREMRANGEBYRANK 才有了明确的淘汰依据。
如果一个 Set 需要控制上限,直接用 ZSet 替代,score 存时间戳,就能复用窗口裁剪逻辑。
Set 是无序的,无法表达"哪个元素该被淘汰";换成 ZSet 用时间戳做 score,就有了淘汰依据,配合
ZREMRANGEBYRANK可以在写入时顺手完成容量裁剪,从根本上避免大 key 问题。
ZREMRANGEBYRANK 是 Redis ZSet(有序集合)的命令,用于按排名范围删除元素。
ZREMRANGEBYRANK key start stop
start/stop是基于排名的索引 ,从0开始,按 score 从小到大排序- 支持负数索引,
-1表示最后一个元素 - 范围是闭区间,即 start 和 stop 都包含在内
- 返回值:被删除的元素数量
常见使用场景
| 场景 | 说明 |
|---|---|
| 排行榜裁剪 | 只保留 Top N,定期删除排名靠后的元素 |
| 限制 ZSet 大小 | 每次写入后执行 ZREMRANGEBYRANK key 0 -(N+1) 控制总量 |
| 滑动窗口清理 | 配合 score 为时间戳,删除过期数据(更推荐用 ZREMRANGEBYSCORE) |
| 消息队列清理 | 处理完的任务按优先级排名后批量移除 |
最常用的组合是 :写入时 ZADD,写入后立即 ZREMRANGEBYRANK key 0 -(N+1) 裁剪,实现一个固定容量的排行榜。