KV 存储
Cloudflare KV 是一个全球分布的键值存储服务,专为读多写少的场景设计。它非常适合存储配置数据、缓存、会话信息等。
KV 简介
什么是 KV?
KV(Key-Value)是一个分布式键值存储,数据存储在边缘节点,读取速度极快。
KV 的特点
全球分布:数据自动复制到全球边缘节点。
读取极快:毫秒级读取延迟。
最终一致性:写入后可能需要几秒才能在全球生效。
适合读多写少:读取免费,写入有费用。
免费额度
| 资源 | 免费额度 |
|---|---|
| 存储空间 | 1GB |
| 读取操作 | 10 万次/天 |
| 写入操作 | 1000 次/天 |
| 列出操作 | 1000 次/天 |
适用场景
- 配置中心
- 功能开关(Feature Flag)
- A/B 测试
- 缓存
- 白名单/黑名单
- 会话存储
快速开始
创建命名空间
npx wrangler kv:namespace create MY_KV
输出会显示命名空间 ID,保存用于配置。
配置绑定
在 wrangler.toml 中添加:
[[kv_namespaces]]
binding = "MY_KV"
id = "your-namespace-id"
基本操作
写入数据
// 简单写入
await env.MY_KV.put('key', 'value');
// 写入 JSON
await env.MY_KV.put('user:1', JSON.stringify({
name: 'John',
email: '[email protected]'
}));
// 设置过期时间(秒)
await env.MY_KV.put('session:abc', 'data', { expirationTtl: 3600 });
// 设置过期时间戳
await env.MY_KV.put('key', 'value', { expiration: Date.now() / 1000 + 3600 });
// 写入元数据
await env.MY_KV.put('file:1', fileContent, {
metadata: { filename: 'image.jpg', type: 'image/jpeg' }
});
读取数据
// 简单读取
const value = await env.MY_KV.get('key');
// 读取 JSON
const user = await env.MY_KV.get('user:1', 'json');
// 读取为 ArrayBuffer
const buffer = await env.MY_KV.get('file:1', 'arrayBuffer');
// 读取为 ReadableStream
const stream = await env.MY_KV.get('file:1', 'stream');
// 读取带元数据
const { value, metadata } = await env.MY_KV.getWithMetadata('file:1');
删除数据
await env.MY_KV.delete('key');
检查键是否存在
const value = await env.MY_KV.get('key');
if (value === null) {
// 键不存在
}
列出键
基本列出
const result = await env.MY_KV.list();
console.log(result.keys); // 键列表
console.log(result.list_complete); // 是否完整
console.log(result.cursor); // 分页游标
前缀过滤
const result = await env.MY_KV.list({ prefix: 'user:' });
分页
let cursor = null;
let allKeys = [];
do {
const result = await env.MY_KV.list({ cursor, limit: 100 });
allKeys = allKeys.concat(result.keys);
cursor = result.list_complete ? null : result.cursor;
} while (cursor);
批量操作
批量写入
await env.MY_KV.put([
{ key: 'key1', value: 'value1' },
{ key: 'key2', value: 'value2' },
{ key: 'key3', value: 'value3' },
]);
实战示例
配置中心
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const configKey = url.searchParams.get('key');
if (request.method === 'GET') {
const config = await env.MY_KV.get(`config:${configKey}`, 'json');
return Response.json(config || {});
}
if (request.method === 'POST') {
const body = await request.json();
await env.MY_KV.put(`config:${configKey}`, JSON.stringify(body));
return Response.json({ success: true });
}
return new Response('Method Not Allowed', { status: 405 });
},
};
功能开关
export default {
async fetch(request, env, ctx) {
const features = await env.MY_KV.get('features', 'json') || {};
const featureName = 'new_ui';
const enabled = features[featureName] === true;
return Response.json({
feature: featureName,
enabled,
});
},
};
会话存储
export default {
async fetch(request, env, ctx) {
const sessionId = request.headers.get('X-Session-ID');
if (!sessionId) {
return new Response('Unauthorized', { status: 401 });
}
const session = await env.MY_KV.get(`session:${sessionId}`, 'json');
if (!session) {
return new Response('Session expired', { status: 401 });
}
// 刷新会话过期时间
await env.MY_KV.put(`session:${sessionId}`, JSON.stringify(session), {
expirationTtl: 3600 // 1 小时
});
return Response.json(session);
},
};
缓存层
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const cacheKey = `cache:${url.pathname}`;
// 尝试从 KV 获取缓存
const cached = await env.MY_KV.get(cacheKey, 'json');
if (cached) {
return Response.json(cached);
}
// 从源获取数据
const data = await fetchDataFromSource(url);
// 存入 KV,缓存 5 分钟
await env.MY_KV.put(cacheKey, JSON.stringify(data), {
expirationTtl: 300
});
return Response.json(data);
},
};
使用 CLI 管理
写入数据
npx wrangler kv:key put --binding=MY_KV "key" "value"
读取数据
npx wrangler kv:key get --binding=MY_KV "key"
删除数据
npx wrangler kv:key delete --binding=MY_KV "key"
列出键
npx wrangler kv:key list --binding=MY_KV
KV vs D1 vs R2
| 特性 | KV | D1 | R2 |
|---|---|---|---|
| 数据模型 | 键值对 | 关系型 | 对象存储 |
| 查询能力 | 键查找 | SQL | 键查找 |
| 一致性 | 最终一致 | 强一致 | 强一致 |
| 适合场景 | 缓存、配置 | 结构化数据 | 文件存储 |
| 读取性能 | 最快 | 快 | 快 |
最佳实践
键命名规范
使用有意义的键前缀:
user:1
user:2
session:abc123
config:theme
cache:api:users
处理大值
KV 单个值最大 25MB,但建议:
- 大于 1MB 的数据使用 R2
- 使用压缩减小体积
过期策略
合理设置过期时间:
- 会话数据:1-24 小时
- 缓存数据:几分钟到几小时
- 配置数据:不设置过期
常见问题
写入后读取不到?
KV 是最终一致性的,写入后可能需要几秒才能在全球生效。
如何清空命名空间?
没有批量删除 API,需要逐个删除或删除整个命名空间重新创建。
KV 适合存储什么?
适合存储:
- 配置数据
- 缓存
- 会话信息
- 功能开关
不适合存储:
- 大文件(用 R2)
- 需要查询的结构化数据(用 D1)
- 频繁更新的数据
参考链接
下一步
完成 KV 学习后,接下来学习 Tunnel,了解如何实现内网穿透。