为什么使用ZSET存储Feed流相关数据?

1 个回答

  • 武如风
    首先你要快乐,其次都是其次。
    ## 作用 使用 ZSET 存储 Feed 流相关数据,是因为它能够直接匹配社交信息流的核心需求: - **按时间或热度排序**:将时间戳、热度分等可比较数值作为 `score`,Feed 项作为 `member`,Redis 可自动维护顺序。 - **支持倒序拉取最新内容**:通过 `ZREVRANGE` 可以方便获取最新的 Feed;通过 `ZRANGE` 可按正序获取历史内容。 - **支持分页和范围查询**:可以基于排名或 `score` 范围查询,适合信息流分页、按时间区间查询等场景。 - **自动去重**:ZSET 的成员具有唯一性,同一个 Feed 项不会被重复添加。 - **减少业务层排序压力**:相比使用 String 或 LIST 存原始数据、再由应用层排序,ZSET 将有序维护和范围读取交给 Redis,更适合高效分页。 - **便于清理历史数据**:可以用时间戳作为 `score`,通过 `ZREMRANGEBYSCORE` 删除指定时间之前的旧 Feed,实现“只保留最近 N 天数据”。 ## 原理 - ZSET 中每一项都由 `score` 和 `member` 组成: - `score` 通常使用毫秒级时间戳,例如 `System.currentTimeMillis()`; - `member` 通常存储帖子 ID 或 Feed 项标识,例如 `post:abc`。 - Redis 根据 `score` 维护有序结构,底层结合跳表等结构支持按分数和排名快速访问,因此可以直接使用 `ZRANGE`、`ZREVRANGE` 完成排序读取。 - 成员唯一而分数可以重复,因此同一时刻可以有多条 Feed,但同一个成员不会重复出现。 - 时间戳建议使用毫秒级,避免秒级时间戳在同一秒内多条内容发生分数冲突。 - 写入新帖时,可将内容同时加入关注者 Feed 和个人主页 Feed,并通过 pipeline、事务批量写入,同时设置 TTL 作为兜底。 - 历史数据可按 `score` 主动裁剪,例如删除 cutoff 之前的数据:保留所有 `score > cutoff` 的 Feed。 - 清理逻辑可以放到 Lua 脚本中原子执行,避免先查询数量再删除时产生并发竞争。 - 当单个 ZSET 成员数达到百万级时,应关注大 key、深度分页和范围删除的性能问题,可按月等维度拆分 key,或采用 `score + id` 游标分页,避免纯 offset 深度分页。
    发布于 2026-10-28 02:43 •IP属地 云南
    举报
    评论
    收藏