当前位置: 首页 > article >正文

基于 Redis 实现社交 Feed 流:收件箱模式 + 时间线滚动查询

本文基于Redis ZSet实现社交平台关注 Feed 流功能采用收件箱模式推模式 完成博客发布时的粉丝消息推送结合时间戳 偏移量实现无感知滚动分页查询解决传统分页卡顿、数据重复 / 丢失问题。附带完整业务代码、核心逻辑解析、Redis 命令详解。一、项目背景与 Feed 流方案选型1.1 什么是 Feed 流1.2 Feed 流主流方案对比拉模式读扩散用户查询时去拉取关注者的内容 → 优点存储小缺点查询慢、高并发卡顿推模式写扩散 / 收件箱模式发布内容时推送给所有粉丝的收件箱 → 优点查询极快缺点写入压力大推拉结合大 V 用拉模式普通用户用推模式本项目选型推模式收件箱模式适合中小型社交项目实现简单、查询性能优异。二、Redis 收件箱模式核心原理2.1 核心思想每个用户拥有一个独立 Redis 收件箱Key用户发布博客 → 遍历所有粉丝 → 将博客 ID 时间戳存入粉丝的 Redis 收件箱用户查看关注流 → 直接从自己的 Redis 收件箱查询数据无需查询数据库2.2 Redis 数据结构选型选用ZSet有序集合Keyfeed:用户ID每个用户独立收件箱Value博客 IDScore发布时间戳实现按时间倒序排序2.3 滚动分页核心逻辑解决传统limit offset分页的痛点新数据插入导致分页重复 / 丢失大数据量 offset 过大性能差滚动分页规则max上一页最后一条数据的时间戳offset与上一页最后一条时间戳相同的元素个数去重每次查询固定条数实现无感知下拉加载三、核心技术栈与数据结构3.1 技术栈SpringBootMyBatis-PlusRedisZSetStringRedisTemplate线程本地变量UserHolder存储登录用户3.2 Redis Key 设计// Feed流收件箱Key前缀 private static final String FEED_KEY feed:; // 完整Keyfeed:10011001为用户ID四、功能实现一发布博客 → 推送到粉丝收件箱4.1 业务流程获取当前登录用户保存博客到数据库查询当前用户的所有粉丝遍历粉丝将博客 ID 时间戳存入粉丝的 Redis 收件箱4.2 完整代码Override public Result saveBlog(Blog blog) { //获取当前登录用户 UserDTO user UserHolder.getUser(); blog.setUserId(user.getId()); //保存探店博文 boolean isSuccess save(blog); if (!isSuccess) { return Result.fail(新增笔记失败); } //查询所有粉丝 ListFollow follows followService.query().eq(follow_user_id, user.getId()).list(); //遍历粉丝列表发送通知 for (Follow follow : follows) { //发送通知 Long userId follow.getUserId(); String key FEED_KEY userId; //TODO ZSet排序规则按时间戳倒序 stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis()); } System.out.println(新增笔记成功); return Result.ok(blog.getId()); }4.3 代码核心解析粉丝查询follow_user_id是被关注者 ID查询所有关注当前用户的粉丝Redis 推送ZSet.add以时间戳为 Score天然支持时间倒序推模式核心写时多一步操作读时极致性能五、功能实现二关注 Feed 流滚动分页查询5.1 业务流程5.2 完整代码Override public Result queryBlogOfFollow(Long max, Integer offset) { // 1. 获取当前用户 Long userId UserHolder.getUser().getId(); // 2. 查询收件箱 ZREV RANGE BY SCORE key max:当前时间戳|上一次最小时间戳 min0 limit offset count数量 String key FEED_KEY userId; //倒序查询同时把score一起返回 2 每页取两条 SetZSetOperations.TypedTupleString typedTuples stringRedisTemplate.opsForZSet() .reverseRangeByScoreWithScores(key, 0, max, offset, 2); if(typedTuples null || typedTuples.isEmpty()){ return Result.ok(Collections.emptyList()); } // 3. 解析数据: blogId、minTime时间戳、offset //offset与上一个查询最后元素相同的元素的个数 ListLong ids new ArrayList(typedTuples.size()); long minTime 0; int os 1; for (ZSetOperations.TypedTupleString tuple : typedTuples) { //添加id到列表 ids.add(Long.valueOf(Objects.requireNonNull(tuple.getValue()))); //获取时间戳 long time Objects.requireNonNull(tuple.getScore()).longValue(); if (time minTime){ os; }else{ minTime time; os 1; } } // 4. 根据id查询blog String idStr StrUtil.join(,,ids); ListBlog blogs query().in(id, ids).last(order by field(id, idStr )).list(); for (Blog blog : blogs) { //查询blog相关用户 queryBlogUser(blog); //查询blog是否被点赞 queryBlogLikes(blog.getId()); } // 5. 封装并返回 ScrollResult sr new ScrollResult(); sr.setList(blogs); sr.setOffset(os); sr.setMinTime(minTime); return Result.ok(sr); }5.3 核心代码逐行解析Redis 查询命令reverseRangeByScoreWithScores按分数倒序查询同时返回 value 和 score参数0 (最小时间) → max (上一页最后时间戳)分页offset (偏移量) → 2 (每页条数)滚动分页参数计算minTime当前页最后一条数据的时间戳下一页的 maxos与 minTime 相同的元素个数下一页的 offset解决同分数据重复数据库查询保持顺序order by field(id, ...)保证查询结果和 Redis 中 ID 顺序一致数据封装补充用户信息、点赞状态返回前端需要的完整数据5.4 前端交互逻辑首次加载max 当前时间戳offset0下拉加载将上一页返回的minTime作为 maxoffset作为 offset无数据时停止加载代码详解stringRedisTemplate中的value只能是字符串ZSet的member也只能是字符串stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMillis());查询key对应的value中分数在0到max之间的元素同时把分数一起返回SetZSetOperations.TypedTupleString typedTuples stringRedisTemplate.opsForZSet() .reverseRangeByScoreWithScores(key, 0, max, offset, 2);从 Redis ZSet 中解析出博客 ID 列表并计算滚动分页必须的两个关键参数minTime 和 offset解决同分数据重复加载问题。如果当前时间是最小时间说明当前数据与上一次查询最后一个数据重复记录重复次数为什么要记录重复次数offset如果在同一时间有大量博客发布时间戳一致下一次查询会重复加载数据返回前端时间戳和offset下一次查询时前端会跳过offset个时间戳一样的数据。for (ZSetOperations.TypedTupleString tuple : typedTuples) { //添加id到列表 ids.add(Long.valueOf(Objects.requireNonNull(tuple.getValue()))); //获取时间戳 long time Objects.requireNonNull(tuple.getScore()).longValue(); if (time minTime){ os; }else{ minTime time; os 1; } }

相关文章:

基于 Redis 实现社交 Feed 流:收件箱模式 + 时间线滚动查询

本文基于Redis ZSet实现社交平台关注 Feed 流功能,采用收件箱模式(推模式) 完成博客发布时的粉丝消息推送,结合时间戳 偏移量实现无感知滚动分页查询,解决传统分页卡顿、数据重复 / 丢失问题。附带完整业务代码、核心…...

网盘直链解析工具:基于JavaScript的八大网盘下载地址获取方案

网盘直链解析工具:基于JavaScript的八大网盘下载地址获取方案 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿里云盘 / 中国移动云盘…...

BitNet-b1.58-2B-4T-GGUF 在计算机组成原理教学中的辅助应用

BitNet-b1.58-2B-4T-GGUF 在计算机组成原理教学中的辅助应用 1. 教学痛点与解决方案 计算机组成原理作为计算机专业的核心基础课程,长期面临概念抽象、实践门槛高的教学挑战。传统教学方式中,学生往往难以直观理解CPU流水线、缓存一致性等复杂机制&…...

YimMenu终极实战指南:免费GTA5辅助工具完整配置与高效使用手册

YimMenu终极实战指南:免费GTA5辅助工具完整配置与高效使用手册 【免费下载链接】YimMenu YimMenu, a GTA V menu protecting against a wide ranges of the public crashes and improving the overall experience. 项目地址: https://gitcode.com/GitHub_Trending…...

DLSS Swapper终极指南:5分钟学会智能管理游戏DLSS文件,彻底告别手动替换烦恼

DLSS Swapper终极指南:5分钟学会智能管理游戏DLSS文件,彻底告别手动替换烦恼 【免费下载链接】dlss-swapper 项目地址: https://gitcode.com/GitHub_Trending/dl/dlss-swapper 你是否曾经为了提升游戏帧率,手动在各个游戏目录中寻找并…...

nli-MiniLM2-L6-H768模型解析:深入理解其轻量级设计背后的计算机组成原理考量

nli-MiniLM2-L6-H768模型解析:深入理解其轻量级设计背后的计算机组成原理考量 1. 引言:为什么需要轻量级模型 在AI模型部署的实践中,我们常常面临一个核心矛盾:模型性能与计算资源消耗之间的权衡。nli-MiniLM2-L6-H768这类轻量级…...

Miniconda-Python3.11镜像:快速搭建Web开发/数据分析环境

Miniconda-Python3.11镜像:快速搭建Web开发/数据分析环境 1. 为什么选择Miniconda-Python3.11 Python作为当下最流行的编程语言之一,在Web开发、数据分析、人工智能等领域都有广泛应用。但Python环境管理一直是个令人头疼的问题——不同项目可能需要不…...

有限差分法模拟地震波场时,如何避免数值不稳定和频散?PML边界设置实战经验分享

有限差分法模拟地震波场的稳定性优化与PML边界实战指南 地震波场数值模拟是地球物理勘探和地震学研究的重要工具,而有限差分法因其实现简单、计算高效成为最常用的数值模拟方法之一。但在实际应用中,数值不稳定和频散问题常常困扰着研究者,尤…...

SNP亮相2026 SAP大消费行业峰会,以数据为核心驱动企业转型升级

2026年4月24日,SAP大消费行业峰会在上海圆满落幕。本次峰会汇聚了大消费、零售、生命科学领域的百余位企业领袖与专家。SNP作为一家致力于数据迁移的专业软件及服务提供商与德勤、海通安恒等核心生态伙伴受邀出席,共同探讨AI时代下的企业增长新路径。AI重…...

别再只懂RBAC了!用ABAC搞定复杂业务权限,看这篇就够了(附Spring Security实战)

从RBAC到ABAC:构建下一代动态权限系统的实战指南 在电商后台系统开发中,你是否遇到过这样的场景:VIP用户只能在促销时段修改特定类目商品价格,而普通管理员仅能在工作日操作非敏感商品?传统RBAC(基于角色的…...

【转行大模型】大龄程序员转行AI大模型:高薪、前沿与实战全攻略

前言 对于大龄程序员而言,转行到AI大模型领域是一个既充满挑战又极具吸引力的选择。在这个领域,您将有机会接触到最新的技术趋势,参与到前沿的项目中,并且有可能获得更高的薪酬。下面是一些具体的步骤和建议,帮助您顺…...

抖音批量下载终极解决方案:从零开始实战,告别繁琐操作

抖音批量下载终极解决方案:从零开始实战,告别繁琐操作 【免费下载链接】douyin-downloader A practical Douyin downloader for both single-item and profile batch downloads, with progress display, retries, SQLite deduplication, and browser fal…...

# 用 Python 构建碳足迹追踪工具:从代码到可视化,实现绿色编程新实践在当前全球关注碳中和的大背景下,**开发者不仅是技术的创

用 Python 构建碳足迹追踪工具:从代码到可视化,实现绿色编程新实践 在当前全球关注碳中和的大背景下,开发者不仅是技术的创造者,更应成为环境可持续性的践行者。本文将带你用 Python 编写一个轻量级但功能完整的 碳足迹计算与分析…...

新手必看:用Mission Planner和QGroundControl调参,手机和电脑哪个更方便?

Mission Planner与QGroundControl实战对比:无人机调参工具选型指南 刚组装完第一台DIY无人机的兴奋感还没消退,我就被一个现实问题难住了——该用电脑上的Mission Planner还是手机端的QGroundControl进行飞控调参?这个问题看似简单&#xff0…...

2 51单片机引脚

一、单片机名称的含义这里以STC 89C52RC40I-PDIP402538HBSB06.X90C为例STC表示厂商——STC公司(宏晶科技)89——8051内核,兼容标准MCS-51指令集C——工作电压,C: 5.5~3.3V 、 LE: 3.6~2.0V52表示型号序号——程序空间ROM大小——5…...

别再只看单个差异基因了!用R语言clusterProfiler包做ORA富集分析,给你的RNA-seq结果找个靠谱的‘解释’

从基因列表到生物学故事:用R语言解锁RNA-seq数据的通路级解读 第一次拿到RNA-seq差异分析结果时,看着Excel里那几百个"显著差异基因",我盯着屏幕发呆了半小时——这些基因到底说明了什么生物学问题?如果你也经历过这种&…...

算法打卡第二十天 / 150.逆波兰表达式求值

一、今日学习任务第20天 栈的经典应用 核心要求:实现逆波兰表达式的求值操作,掌握栈这一核心解法,理解栈在表达式计算中的底层逻辑。 前置建议:回顾栈的基础数据结构与进出栈操作,理解逆波兰表达式(后缀表达…...

像说话一样写程序:图解 Python 常用基础语法

把代码当成日常对话 很多人一看到编程代码,脑海里浮现的往往是复杂的数学公式或者晦涩的机器指令,瞬间就产生了畏难情绪。其实,Python 之所以被称为“可执行的伪代码”,就是因为它的设计初衷是让程序员像说话一样去表达逻辑。我们…...

从零开始写代码:Python 基础语法快速上手攻略

变量与数据类型:给数据贴上标签 编程的第一步,就是学会如何“存储”和“识别”数据。在 Python 中,你不需要像其他语言那样声明复杂的类型,只需给数据起个名字(变量),Python 会自动识别它是数字…...

旋转机械故障诊断特征表达与智能识别【附代码】

✅ 博主简介:擅长数据搜集与处理、建模仿真、程序设计、仿真代码、论文写作与指导,毕业论文、期刊论文经验交流。 ✅ 如需沟通交流,扫描文章底部二维码。(1)优化变分互无量纲特征与变分模态分解的联合特征提取&#xf…...

终极指南:5分钟掌握KMS智能激活工具,永久告别Windows和Office激活烦恼

终极指南:5分钟掌握KMS智能激活工具,永久告别Windows和Office激活烦恼 【免费下载链接】KMS_VL_ALL_AIO Smart Activation Script 项目地址: https://gitcode.com/gh_mirrors/km/KMS_VL_ALL_AIO 你是否曾因Windows系统频繁弹出激活提醒而分心工作…...

PyWxDump技术剖析:数据解密工具的合规边界与安全启示

PyWxDump技术剖析:数据解密工具的合规边界与安全启示 【免费下载链接】PyWxDump 删库 项目地址: https://gitcode.com/GitHub_Trending/py/PyWxDump 技术挑战与应对策略的双重博弈 在数字隐私与数据安全日益重要的今天,微信数据解密工具PyWxDump…...

告别扫描PDF无法搜索的困扰:OCRmyPDF让你的文档“开口说话“

告别扫描PDF无法搜索的困扰:OCRmyPDF让你的文档"开口说话" 【免费下载链接】OCRmyPDF OCRmyPDF adds an OCR text layer to scanned PDF files, allowing them to be searched 项目地址: https://gitcode.com/GitHub_Trending/oc/OCRmyPDF 你是否曾…...

三步告别魔兽争霸3闪退:WarcraftHelper现代兼容性修复指南

三步告别魔兽争霸3闪退:WarcraftHelper现代兼容性修复指南 【免费下载链接】WarcraftHelper Warcraft III Helper , support 1.20e, 1.24e, 1.26a, 1.27a, 1.27b 项目地址: https://gitcode.com/gh_mirrors/wa/WarcraftHelper 你是否曾满怀期待地打开魔兽争霸…...

我劝你,别再无脑用 TeamViewer 和 ToDesk 了

远程办公、异地协助、帮家里人修电脑,这几年几乎成了很多人的日常需求。 以前大家图省事,装个 TeamViewer、ToDesk,登录一下就能连,确实方便。但时间一长,问题也越来越明显:• 免费版限制越来越多• 稍微用…...

保姆级教程:在野火STM32F429上用HAL库搞定LVGL 8.2移植(附触摸屏适配避坑)

野火STM32F429开发板LVGL 8.2移植实战指南 拿到野火STM32F429挑战者开发板和5寸电容屏,想快速搭建LVGUI开发环境却卡在HAL库配置、文件结构组织、触摸驱动适配等问题上?这篇保姆级教程将带你一步步完成LVGL 8.2在STM32F429平台上的完整移植,特…...

PvZ Toolkit:植物大战僵尸修改器完整使用指南,5大功能让你轻松掌控游戏

PvZ Toolkit:植物大战僵尸修改器完整使用指南,5大功能让你轻松掌控游戏 【免费下载链接】pvztoolkit 植物大战僵尸 PC 版综合修改器 项目地址: https://gitcode.com/gh_mirrors/pv/pvztoolkit 还在为植物大战僵尸中的阳光不够用而烦恼吗&#xff…...

开源鸿蒙 Flutter 实战|ShimmerSkeleton 骨架屏编译错误全流程修复与最佳实践

🛠️ 开源鸿蒙 Flutter 实战|ShimmerSkeleton 骨架屏编译错误全流程修复与最佳实践 欢迎加入开源鸿蒙跨平台社区→https://openharmonycrosplatform.csdn.net 【摘要】本文面向开源鸿蒙跨平台开发新手,针对 Flutter 鸿蒙端构建时出现的Shimme…...

TLF35584的ABIST自检功能怎么用?一个案例讲透模拟故障注入与诊断覆盖率的验证

TLF35584 ABIST自检实战:如何通过模拟故障注入验证诊断覆盖率 在汽车电子系统的功能安全开发中,诊断覆盖率验证是一个绕不开的硬性要求。ISO 26262标准明确要求对硬件故障检测机制的有效性进行量化评估,而传统方法往往需要复杂的硬件故障注入…...

Flowchart-Vue:如何快速构建专业级流程图应用

Flowchart-Vue:如何快速构建专业级流程图应用 【免费下载链接】flowchart-vue Vue.js Flowchart Component with Drag-and-Drop Designer 项目地址: https://gitcode.com/gh_mirrors/fl/flowchart-vue 在现代Web开发中,流程图可视化是许多业务系统…...