Vercel × Upstash Redis 详解
约 1931 字大约 6 分钟
2026-05-22
简介
Upstash Redis 是一个 Serverless Redis 服务,对外提供 HTTP / REST API,可以在任何能发起 fetch 请求的运行环境中使用——包括传统 Node.js 服务、AWS Lambda、Cloudflare Workers、Vercel Edge Middleware、浏览器、甚至 WebAssembly。
2024 年 12 月,Vercel 关闭了自家的 Vercel KV 产品,将所有已有 KV 数据库自动迁移到了 Upstash Redis。从那以后,Vercel 官方推荐的「KV 键值存储」方案就是通过 Vercel Marketplace 一键集成 Upstash Redis:项目和数据库由 Vercel 统一计费,环境变量自动注入。
简单来说:Vercel 提供运行时,Upstash 提供数据层,两者通过 Marketplace 深度绑定,是目前在 Vercel 上做缓存、会话、限流、计数器等场景最顺手的方案。
解决什么问题?
传统 Redis 在 Serverless / Edge 场景下有几个硬伤:
- 连接管理失控:原生 Redis 使用长连接 TCP 协议,每个 Lambda 冷启动都要重新握手;高并发下连接数迅速爆掉。
- 冷启动延迟高:Edge Function 是短生命周期的,建连 → 鉴权 → 查询 → 关闭,每次都要走一遍。
- VPC / 网络隔离限制:Edge Runtime(V8 isolate)只允许 fetch,传统 TCP 客户端无法运行。
- 按时计费不划算:低频小流量的场景下,按小时购买 Redis 实例性价比极低。
Upstash 的解法:
| 痛点 | Upstash 方案 |
|---|---|
| 连接管理 | HTTP 无连接,每次请求独立,无需连接池 |
| 冷启动 | REST API 直接 fetch,无握手开销 |
| Edge 兼容 | Edge Runtime 原生支持 fetch,开箱即用 |
| 成本模型 | Pay-per-request:每次命令计费,闲置 0 成本 |
| 全球分布 | Global Database 提供跨 region 多副本读,延迟 < 50ms |
核心概念
| 概念 | 说明 |
|---|---|
| REST API | Upstash 在 Redis 协议之上封装了一层 HTTPS 端点,每条命令是一次 HTTP 请求 |
| Regional Database | 单 region 部署,写延迟最低,适合区域性应用 |
| Global Database | 多 region 副本,读取就近,写入主节点同步,适合 Edge 场景 |
| REST URL / Token | 数据库的两个核心凭据,分别注入为 UPSTASH_REDIS_REST_URL 和 UPSTASH_REDIS_REST_TOKEN |
| Read-only Token | 可选的只读凭据,用于客户端直连等只读场景 |
| Pipeline | 批量打包多条命令为一个 HTTP 请求,减少往返次数 |
| Eviction | 数据库容量到达上限时的逐出策略(noeviction / allkeys-lru 等) |
| TLS | 全部流量强制 HTTPS,无需额外配置 |
除 Redis 外,Upstash 还提供 QStash(消息队列 / 定时任务)、Vector(向量数据库)、Search(搜索),同样以 REST API 提供,体系一脉相承。
安装与配置
方式一:通过 Vercel Marketplace(推荐)
- 进入 Vercel 项目 → Storage → Create Database → 选择 Upstash for Redis。
- 选择由 Vercel 托管账户(统一计费)或链接现有 Upstash 账户。
- 选择 Region、Plan、容量后创建。
- Vercel 会自动将以下环境变量注入到选定的 Vercel 项目中:
UPSTASH_REDIS_REST_URL=https://xxx.upstash.io
UPSTASH_REDIS_REST_TOKEN=xxxxxxxxxxxxxxxx- 重新部署项目让环境变量生效。
方式二:直接在 Upstash Console 创建
- 注册 console.upstash.com。
- 创建数据库后在 Details 页复制
REST URL和REST Token。 - 手动写入
.env.local,并在 Vercel Dashboard 同步添加 production / preview 环境变量。
安装 SDK
pnpm add @upstash/redis
# 如需做限流
pnpm add @upstash/ratelimit使用方式
初始化
import { Redis } from '@upstash/redis';
// 方式 1:从环境变量自动读取(推荐)
const redis = Redis.fromEnv();
// 方式 2:显式传参
const redis = new Redis({
url: process.env.UPSTASH_REDIS_REST_URL!,
token: process.env.UPSTASH_REDIS_REST_TOKEN!,
});常用命令
SDK 的方法签名与原生 Redis 命令对齐,返回 Promise。
// String
await redis.set('user:1', { name: 'ZhenYu' }); // 自动 JSON 序列化
const user = await redis.get<{ name: string }>('user:1'); // 自动反序列化
// 计数器
await redis.incr('views:home');
// 过期时间(秒)
await redis.set('session:abc', token, { ex: 3600 });
// Hash
await redis.hset('profile:1', { name: 'ZhenYu', age: 30 });
const profile = await redis.hgetall('profile:1');
// List
await redis.lpush('queue', 'job-1', 'job-2');
const job = await redis.rpop('queue');
// Sorted Set
await redis.zadd('leaderboard', { score: 100, member: 'alice' });
const top = await redis.zrange('leaderboard', 0, 9, { rev: true });Pipeline
把多条命令打包成一次 HTTP 请求,强烈推荐用于多步骤操作,可显著降低延迟和请求成本:
const p = redis.pipeline();
p.set('a', 1);
p.incr('a');
p.get('a');
const [, , value] = await p.exec<[string, number, number]>();
// value === 2Transaction(MULTI/EXEC)
const tx = redis.multi();
tx.incr('counter');
tx.expire('counter', 60);
await tx.exec();注意:因为底层是无状态 HTTP,Upstash 的 transaction 是服务端原子执行的,与原生 Redis 行为一致。
实际示例
1. Edge Middleware 全球速率限制
@upstash/ratelimit 与 Redis 配合,可以在 Vercel Edge Middleware 中实现 请求未进入 Serverless Function 前就拦截,省钱又快。
// middleware.ts
import { Ratelimit } from '@upstash/ratelimit';
import { Redis } from '@upstash/redis';
import { NextResponse } from 'next/server';
export const config = { matcher: '/api/:path*' };
const ratelimit = new Ratelimit({
redis: Redis.fromEnv(),
limiter: Ratelimit.slidingWindow(10, '10 s'), // 10 秒 10 次
analytics: true,
prefix: 'rl:api',
});
export default async function middleware(req: Request) {
const ip = req.headers.get('x-forwarded-for') ?? 'anonymous';
const { success, limit, remaining, reset } = await ratelimit.limit(ip);
if (!success) {
return new NextResponse('Too Many Requests', {
status: 429,
headers: {
'X-RateLimit-Limit': String(limit),
'X-RateLimit-Remaining': String(remaining),
'X-RateLimit-Reset': String(reset),
},
});
}
return NextResponse.next();
}slidingWindow 之外还有 fixedWindow、tokenBucket、cachedFixedWindow 等算法,按业务取舍。
2. Next.js App Router 接口缓存
// app/api/posts/[id]/route.ts
import { Redis } from '@upstash/redis';
import { NextResponse } from 'next/server';
const redis = Redis.fromEnv();
export async function GET(_: Request, { params }: { params: { id: string } }) {
const key = `post:${params.id}`;
const cached = await redis.get(key);
if (cached) return NextResponse.json(cached);
const post = await db.post.findUnique({ where: { id: params.id } });
await redis.set(key, post, { ex: 60 }); // 缓存 60 秒
return NextResponse.json(post);
}3. 会话存储
import { Redis } from '@upstash/redis';
import { cookies } from 'next/headers';
const redis = Redis.fromEnv();
const SESSION_TTL = 60 * 60 * 24 * 7; // 7 天
export async function createSession(userId: string) {
const sid = crypto.randomUUID();
await redis.set(`sess:${sid}`, { userId }, { ex: SESSION_TTL });
cookies().set('sid', sid, { httpOnly: true, secure: true, maxAge: SESSION_TTL });
return sid;
}
export async function readSession() {
const sid = cookies().get('sid')?.value;
if (!sid) return null;
return redis.get<{ userId: string }>(`sess:${sid}`);
}4. 分布式锁
async function withLock<T>(key: string, ttl: number, fn: () => Promise<T>) {
const ok = await redis.set(key, '1', { nx: true, ex: ttl });
if (!ok) throw new Error('Locked');
try {
return await fn();
} finally {
await redis.del(key);
}
}
await withLock('cron:daily-report', 60, async () => {
// 临界区代码
});最佳实践
- 能 Pipeline 就 Pipeline。每次 REST 调用都是真实的 HTTP 请求,合并能直接降低延迟和按请求计费的成本。
- Edge 场景选 Global Database。读取就近,写主同步;写少读多的缓存最受益。读写比悬殊或全球用户分布广时差距明显。
- REST URL 含 region 信息,不要硬编码,永远从环境变量读,方便区域迁移。
- 不要把 Token 暴露到客户端。如果一定需要浏览器直连,使用 Console 里生成的 Read-only Token,并搭配带前缀的 key 命名空间。
- 永远设置 TTL。Upstash 按存储量也会计费,无过期的 key 会让账单悄悄上涨。可以在数据库层面配合
allkeys-lru逐出策略兜底。 - 限流前缀分业务。
@upstash/ratelimit的prefix字段建议按业务线划分,方便监控和清理。 - 观察 Analytics 面板。Upstash Console 的请求量、命中率、Top Keys 都是性能诊断的好抓手。
- 跨环境隔离。开发 / 预览 / 生产应使用不同的数据库;Vercel Marketplace 允许把同一 Upstash DB 绑定到多个 Vercel 环境,但脏数据风险高,不推荐共用。
- 本地开发联调。
.env.local直接写 production 同款 URL/Token 即可(HTTP 没有网络隔离问题),无需起 docker。
计费要点
| 模式 | 计费 | 适用 |
|---|---|---|
| Pay-as-you-go | 每天 10K 命令免费额度,之后约 $0.2 / 100K 命令 | 起步、流量波动大、低频应用 |
| Fixed | 按月固定容量与命令数 | 流量稳定、可预测的中大型应用 |
| Pro / Enterprise | 独立资源、SLA、VPC Peering | 大规模生产 |
通过 Vercel Marketplace 创建的实例,所有费用统一进入 Vercel 账单,不需要单独的 Upstash 信用卡。
相关链接
- 官网:https://upstash.com
- Redis 文档:https://upstash.com/docs/redis
- Vercel 集成指南:https://upstash.com/docs/redis/howto/vercelintegration
- Vercel Marketplace:https://vercel.com/marketplace/upstash
- Vercel Redis 文档:https://vercel.com/docs/redis
@upstash/redisSDK:https://github.com/upstash/redis-js@upstash/ratelimitSDK:https://github.com/upstash/ratelimit- 定价:https://upstash.com/pricing/redis
