接入Redis缓存后无限滚动分页出现数据重复如何解决
问题根因
数据重复的核心原因是后端Redis缓存逻辑存在多处设计错误,和前端无限滚动逻辑无关,具体问题如下:
- 缓存键规则完全错误:使用
keys("Blog*")全量匹配所有带Blog前缀的缓存键,完全忽略当前请求的搜索关键词q、页码page参数,只要Redis中存在任意相关缓存,就会把所有历史缓存的文章全量返回,不同搜索词、不同页码的请求会拿到完全相同的缓存数据,必然出现重复。 - 写缓存逻辑存在bug:遍历当前页查询结果时,每次循环返回的是整个
searcharticles数组而非单条文章,且缓存key使用随机uuid生成,没有和搜索条件、页码绑定,后续请求根本无法精准匹配对应分页的缓存。 - 缓存无维度隔离:现有缓存是全局共享状态,搜索A关键词存入的内容会被搜索B关键词的请求读取,第一页存入的内容会被第二页及以后的请求读取,完全不符合分页搜索的缓存设计要求。
- 额外性能问题:
keys命令会全量遍历Redis键空间,生产环境使用会阻塞Redis服务,严重影响性能。
修复方案
后端逻辑修复
- 重构缓存key规则,必须绑定搜索词、页码两个核心维度,固定格式为
search:${搜索词}:page:${页码},实现不同搜索条件、不同分页的缓存完全隔离。 - 废弃
keys("Blog*")全量扫库逻辑,直接按照拼接好的固定key读取对应缓存即可。 - 取消单篇文章遍历写缓存的逻辑,数据库查询到整页结果后,直接将整页响应数据序列化存入对应缓存key,同时设置合理的过期时间避免数据长期不一致。
- 移除冗余的
async.map遍历逻辑,减少不必要的异步开销。
修复后的后端参考代码:
const searchpost = async (req, res) => { const perpage = 3; const value = req.query.q || ''; const pageNumber = parseInt(req.query.page) || 1; // 生成绑定搜索维度、分页维度的唯一缓存key const cacheKey = `search:${value}:page:${pageNumber}`; try { // 直接读取对应key的缓存,不做全库扫描 const cacheContent = await client.get(cacheKey); if (cacheContent) { return res.status(200).json(JSON.parse(cacheContent)); } // 缓存未命中时查询数据库 const searcharticles = await Blog.find({ $or: [ { title: { $regex: value, $options: "i" } }, { tag: { $regex: value, $options: "i" } }, { Subtitle: { $regex: value, $options: "i" } }, ], }) .skip((pageNumber - 1) * perpage) .limit(perpage) .populate("authorId"); const responseData = { searcharticles }; // 写入缓存并设置10分钟过期时间 await client.setEx(cacheKey, 600, JSON.stringify(responseData)); res.status(200).json(responseData); } catch (err) { res.status(500).json({ message: "hata durumu oluştu" }); } };
前端去重逻辑优化
现有前端去重逻辑存在缺陷:new Set()判断对象重复时是按引用地址判断,内容完全相同的不同对象不会被识别为重复项,需要改为按文章唯一ID去重,修改setBlogs部分代码即可:
setBlogs((prevBlog) => { const mergedList = [...prevBlog, ...res.data.searcharticles]; // 以文章_id为唯一键去重 const uniqueBlogs = new Map(); mergedList.forEach(blog => uniqueBlogs.set(blog._id, blog)); return Array.from(uniqueBlogs.values()); });
内容的提问来源于stack exchange,提问作者Nurettin Şenssabc
相关产品推荐
相关产品推荐

