教程 / 后端集成 / 使用 Upstash 实现缓存与限流
📝 文字 ● 中级 更新于 2026-05-13

使用 Upstash Redis 实现缓存与限流

Upstash 是基于 HTTP 的无服务器 Redis。无需连接池、无需保持端口开放、无需维护空闲实例。缓存热点查询、限制恶意调用方、将后台任务排入队列——仅按请求数计费。

何时选择 Upstash

0
  • 适用场景 — 当你在 Vercel/Cloudflare/Lambda 上运行,无法在调用之间保持 TCP Redis 连接时。他们的 HTTP/REST API 是在边缘函数中能使用的唯一 Redis 方式。
  • 替代方案Redis Cloud(官方选项,仅 TCP,价格较高),Redis on Railway/Fly/Render(自行运维,便宜),在 VPS 上自托管(最便宜,工作量最大),Cloudflare KV(最终一致性,边缘复制,API 功能较窄)。
  • 免费额度 — 每天 10K 请求,256 MB 存储。足以支撑一个真实产品上线。
  • 注册入口console.upstash.com/login,支持 GitHub 登录,无需绑定信用卡。

创建数据库

1

进入控制台,点击 Create database,选择以下配置:

  • 类型 — Regional(更便宜,单区域,延迟约 5–20ms)或 Global(多区域复制,全球延迟约 5ms,费用较高)
  • 区域 — 选择离你的服务器最近的区域,而非离用户最近的(你的应用本身已经负责路由用户流量)
  • 淘汰策略 — 保持开启。若触及内存上限,最旧的键会被淘汰,而不是写入失败

创建完成后,你会看到 UPSTASH_REDIS_REST_URLUPSTASH_REDIS_REST_TOKEN,将两者都添加到 .env 文件中。

缓存热点查询

2

安装客户端:

npm install @upstash/redis

将任何耗时查询包裹起来:

import { Redis } from "@upstash/redis";

const redis = Redis.fromEnv();

async function getTopProducts() {
  const cached = await redis.get<Product[]>("top-products");
  if (cached) return cached;

  const fresh = await db.query("SELECT * FROM products ORDER BY sales DESC LIMIT 20");
  await redis.set("top-products", fresh, { ex: 60 }); // 60s TTL
  return fresh;
}

关键使用模式:

  • 始终设置 TTLex 单位为秒)。不设置意味着永久缓存,而过期数据比慢数据更糟糕。
  • 缓存问题的答案,而非整个对象 — 用 user:123:plan 而非 user:123。这样可以只失效某个字段,而不必清除全部缓存。
  • 写入时主动失效 — 当源数据行发生变更时,调用 redis.del(key)。不要单靠 TTL 来处理用户可见的数据。
  • 缓存空结果 — "没有找到记录"同样值得短暂缓存。否则 404 路径反而会成为你最慢的路径。

完整 API 参考:Upstash TS SDK 文档。命令与 Redis 标准一一对应:getsetincrlpushzadd 等。

对任意端点进行限流

3

安装限流辅助库:

npm install @upstash/ratelimit

以下中间件将每个 IP 限制为每 10 秒最多 10 次请求(滑动窗口):

import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";

const ratelimit = new Ratelimit({
  redis: Redis.fromEnv(),
  limiter: Ratelimit.slidingWindow(10, "10 s"),
  analytics: true, // dashboard graphs
});

// Express
app.use(async (req, res, next) => {
  const ip = req.headers["x-forwarded-for"] || req.socket.remoteAddress;
  const { success, limit, remaining, reset } = await ratelimit.limit(ip);

  res.setHeader("X-RateLimit-Limit", limit);
  res.setHeader("X-RateLimit-Remaining", remaining);
  res.setHeader("X-RateLimit-Reset", reset);

  if (!success) {
    return res.status(429).json({ error: "Too many requests" });
  }
  next();
});

限流器选项:

  • fixedWindow(n, "1 m") — 按固定分钟计数,开销最低,但在窗口边界容易出现流量突发。
  • slidingWindow(n, "10 s") — 平滑限流,可防止边界突发。默认推荐。
  • tokenBucket(n, "1 s", capacity) — 允许最多 capacity 的突发流量,以指定速率匀速补充。适用于允许短时突发的 API。

标识符选择:

  • IP — 粒度较粗;VPN/代理可绕过;但仍能拦截爬虫级别的滥用行为
  • 用户 ID — 针对已认证路由的首选方式
  • API Key — 公开 API 的必选方案,支持为每个 Key 单独设置限额
  • 两者结合 — 使用不同窗口的独立限流器(例如"每 IP 每分钟 5 次,同时每用户每小时 100 次")

在 Cloudflare Workers / Vercel Edge 中使用

4

两个 SDK 在边缘运行时中均可直接使用——基于 HTTP/REST 意味着无需 TCP socket。在你的平台中配置环境变量:

  • Vercel — Project → Settings → Environment Variables。Upstash Vercel 集成可自动填充这些变量。
  • Cloudflare Workerswrangler secret put UPSTASH_REDIS_REST_URL + wrangler secret put UPSTASH_REDIS_REST_TOKEN
  • Fly.iofly secrets set UPSTASH_REDIS_REST_URL=…

使用 QStash 处理后台任务

5

如果你还需要延迟或定时任务,QStash 就在同一个控制台中提供消息队列功能。它是一个基于 HTTP 的任务执行器:你发送一个任务,QStash 会持续重试,直到你的端点返回 200。

import { Client } from "@upstash/qstash";

const qstash = new Client({ token: process.env.QSTASH_TOKEN });

// Send "welcome email" 5 min after signup
await qstash.publishJSON({
  url: "https://yourapp.com/api/jobs/welcome-email",
  body: { userId: user.id },
  delay: 300, // seconds
});

替代方案:Inngest(更面向工作流,调试体验更好)、Trigger.devTemporal(企业级)。QStash 是其中最简单的选择。

常见故障

6
  • 当天免费额度耗尽 — 每天 10K 请求 ≈ 每分钟约 7 次。若限流器每个请求消耗 2 次 Redis 读取,额度很快就会耗尽。可以升级方案($0.20/10 万次请求),或在短时间窗口内将限流检查结果缓存在内存中。
  • 写入后缓存未更新 — 数据库已更新,但缓存仍返回旧数据。请在写入路径上添加 redis.del(cacheKey),而不是单靠 TTL。
  • 限流误伤共享 NAT 后的真实用户 — 高校、企业代理、移动运营商可能让数千用户共用同一个 IP。对于面向消费者的应用,一旦建立身份验证,应改为按用户 ID 限流;仅对未认证路由使用 IP 限流。
  • 多区域写入与读取不在同一区域 — Regional 数据库只存在于单一区域。要么升级到 Global 套餐(多区域复制,约 $0.40/10 万次请求),要么将工作负载迁移到与数据库相同的区域。
  • Vercel 冷启动延迟 — 冷启动后的第一次 Redis 调用约需 80ms(TLS 握手 + 区域路由),后续调用约 5ms。不要用单次请求来衡量缓存命中率。
  • Token 暴露在客户端代码中UPSTASH_REDIS_REST_TOKEN 具有管理员级别权限,绝不能暴露给浏览器。如需客户端限流,请使用服务端代理或 Upstash 的只读 Token

定价实情

7
  • 免费套餐 — 每天 10K 请求,256 MB 存储,单区域。足以支撑真实产品。
  • 按量付费 — $0.20/10 万次请求,$0.25/GB 存储/月,无最低消费。大多数小应用每月花费不超过 $5。
  • Global(多区域) — $0.40/10 万次请求。如需在全球任意位置实现 <20ms 读取延迟,可选此套餐。
  • Pro — $280/月固定费用,包含 1 亿次请求和专属资源。适用于按量付费成本波动较大时。
  • QStash — 单独计费:每天免费 500 条消息,超出部分 $1/10 万条。

价格详情:upstash.com/pricing

官方参考资料

核心理解。 Redis 是一个带数据结构的键值存储(列表、集合、有序集合、哈希、流)。缓存只是最常见的用法。一旦配置好,你会发现到处都能用上它:会话存储、排行榜、发布/订阅、去重窗口、功能开关覆盖。