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

Next.js中如何缓存每日仅更新一次的数据集?

NBA赛事热力图数据缓存优化方案

问题背景

用Next.js开发NBA赛事网站,PostgreSQL数据库存储约7万条赛事记录,通过tRPC的getGames接口全量获取数据渲染热力图,每次加载耗时约10秒。数据仅在每日赛事结束后更新一次,需要实现1天的缓存来减少加载时间和数据库请求。尝试了React Query的staleTime/gcTime以及tRPC文档中的请求头配置,未达到预期效果。

当前核心代码

tRPC API接口

getGames: publicProcedure.query(async ({ ctx }) => {
    const allGames = await ctx.db.query.nbaGames.findMany();

    return {
      games: allGames,
    };
  }),

组件数据获取

const { data: gamesData, isLoading: loadingGamesData } = api.game.getGames.useQuery();

优化方案

1. 客户端:React Query 精准缓存配置

全局默认配置可能被单个查询的配置覆盖,建议直接在useQuery中单独设置缓存参数,确保优先级更高:

const { data: gamesData, isLoading: loadingGamesData } = api.game.getGames.useQuery({
  staleTime: 24 * 60 * 60 * 1000, // 1天
  gcTime: 24 * 60 * 60 * 1000, // 1天
  cacheTime: 24 * 60 * 60 * 1000, // 兼容旧版React Query
});

若不需要全局默认缓存规则,可移除TRPC Provider中queryClient的全局staleTime/gcTime配置,避免干扰单个查询的设置。

2. 服务端:tRPC接口+数据库查询缓存

2.1 给tRPC接口设置响应缓存头

在tRPC的procedure层面直接设置响应头,比客户端传请求头更可靠:

import { publicProcedure } from '../trpc';

const ONE_DAY = 24 * 60 * 60;

getGames: publicProcedure
  .middleware(async ({ ctx, next }) => {
    // 设置响应缓存头
    ctx.res?.setHeader('Cache-Control', `s-maxage=${ONE_DAY}, stale-while-revalidate=${ONE_DAY}`);
    return next();
  })
  .query(async ({ ctx }) => {
    const allGames = await ctx.db.query.nbaGames.findMany();
    return { games: allGames };
  }),

2.2 缓存数据库查询结果

直接缓存7万条数据的查询结果,避免每次请求都扫描全表(以Redis为例):

import Redis from 'ioredis';
const redis = new Redis(process.env.REDIS_URL);

getGames: publicProcedure.query(async ({ ctx }) => {
  const cacheKey = 'nba:all-games';
  // 先查缓存
  const cachedGames = await redis.get(cacheKey);
  if (cachedGames) {
    return { games: JSON.parse(cachedGames) };
  }
  // 缓存不存在则查数据库
  const allGames = await ctx.db.query.nbaGames.findMany();
  // 存入缓存,过期时间1天
  await redis.set(cacheKey, JSON.stringify(allGames), 'EX', 24 * 60 * 60);
  return { games: allGames };
}),

每日赛事结束后,手动删除Redis中的nba:all-games缓存键,触发重新查询。

3. Next.js 增量静态再生(ISR)

利用Next.js的ISR预渲染页面,直接生成包含热力图数据的静态页面,用户访问时无需请求API或数据库:

3.1 页面组件中预获取数据

在页面的getStaticProps中获取数据,并设置revalidate为1天:

import { api } from '../utils/api';
import { createContext } from '../server/context';

export async function getStaticProps() {
  const ctx = await createContext();
  const { games } = await api.game.getGames.query(undefined, ctx);
  
  return {
    props: { games },
    revalidate: 24 * 60 * 60, // 1天后自动重新生成页面
  };
}

export default function GamesHeatmapPage({ games }) {
  // 直接用props中的games渲染热力图,无需再调用useQuery
  return <Heatmap data={games} />;
}

3.2 手动触发重新生成

如果需要在每日赛事结束后立即更新,可写一个API路由触发重新生成:

// pages/api/revalidate.ts
export default async function handler(req, res) {
  if (req.query.secret !== process.env.REVALIDATE_SECRET) {
    return res.status(401).json({ message: 'Invalid secret' });
  }

  try {
    await res.revalidate('/'); // 替换为你的热力图页面路径
    return res.json({ revalidated: true });
  } catch (err) {
    return res.status(500).send('Error revalidating');
  }
}

每日赛事结束后调用这个API,即可立即更新页面数据。

优先级建议

优先采用Next.js ISR方案,能最大程度减少客户端加载时间和数据库请求;其次结合Redis缓存数据库查询结果,进一步降低服务端压力;最后确保客户端React Query配置正确,提升用户二次访问的体验。

内容的提问来源于stack exchange,提问作者hakc333

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.18 10:35:02