You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

接入Redis缓存后无限滚动分页出现数据重复如何解决

问题根因

数据重复的核心原因是后端Redis缓存逻辑存在多处设计错误,和前端无限滚动逻辑无关,具体问题如下:

  • 缓存键规则完全错误:使用keys("Blog*")全量匹配所有带Blog前缀的缓存键,完全忽略当前请求的搜索关键词q、页码page参数,只要Redis中存在任意相关缓存,就会把所有历史缓存的文章全量返回,不同搜索词、不同页码的请求会拿到完全相同的缓存数据,必然出现重复。
  • 写缓存逻辑存在bug:遍历当前页查询结果时,每次循环返回的是整个searcharticles数组而非单条文章,且缓存key使用随机uuid生成,没有和搜索条件、页码绑定,后续请求根本无法精准匹配对应分页的缓存。
  • 缓存无维度隔离:现有缓存是全局共享状态,搜索A关键词存入的内容会被搜索B关键词的请求读取,第一页存入的内容会被第二页及以后的请求读取,完全不符合分页搜索的缓存设计要求。
  • 额外性能问题:keys命令会全量遍历Redis键空间,生产环境使用会阻塞Redis服务,严重影响性能。
修复方案

后端逻辑修复

  1. 重构缓存key规则,必须绑定搜索词、页码两个核心维度,固定格式为search:${搜索词}:page:${页码},实现不同搜索条件、不同分页的缓存完全隔离。
  2. 废弃keys("Blog*")全量扫库逻辑,直接按照拼接好的固定key读取对应缓存即可。
  3. 取消单篇文章遍历写缓存的逻辑,数据库查询到整页结果后,直接将整页响应数据序列化存入对应缓存key,同时设置合理的过期时间避免数据长期不一致。
  4. 移除冗余的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 23:27:18