mcpField
C
cloudflare-kv

cloudflare-kv

github.com/jezweb数据与数据库1.1k355 次安装3 周前更新
代码仓库

关于 cloudflare-kv

这项技能面向使用 Cloudflare Workers KV 的开发者,介绍键值操作、缓存、分页、一致性行为、Wrangler 命令和使用限制。

主要功能

  1. 01读取、写入、列出和删除键
  2. 02缓存与元数据模式
  3. 03分页列出键
  4. 04一致性与速率限制指南
  5. 05Wrangler CLI 命令示例

适用场景

  1. 01缓存频繁读取的数据
  2. 02存储配置和功能标志
  3. 03按前缀分页遍历键
  4. 04本地开发时连接远程命名空间

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/jezweb/claude-skills --skill cloudflare-kv

安装后在 Claude Code 中输入/cloudflare-kv确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

README.md
.claude-plugin/
plugin.json
references/
best-practices.md
workers-api.md
rules/
cloudflare-kv.md
templates/
kv-basic-operations.ts
kv-caching-pattern.ts
kv-list-pagination.ts
kv-metadata-pattern.ts

详细介绍

Cloudflare Workers KV

状态:已可用于生产环境 ✅ 最后更新:2026-01-20 依赖项:cloudflare-worker-base(用于 Worker 设置) 最新版本:[email protected],@cloudflare/[email protected]

近期更新(2025):

  • 2025 年 8 月:架构重新设计(性能提升 40 倍,p99 延迟 <5ms,采用与 R2 混合存储)
  • 2025 年 4 月:批量读取 API(单次请求最多检索 100 个键,计为 1 次操作)
  • 2025 年 1 月:命名空间限额提高(Free 和 Paid 套餐的每个账户命名空间数从 200 个增至 1,000 个)

快速入门(5 分钟)

# Create namespace
npx wrangler kv namespace create MY_NAMESPACE
# Output: [[kv_namespaces]] binding = "MY_NAMESPACE" id = "<UUID>"

wrangler.jsonc:

{
  "kv_namespaces": [{
    "binding": "MY_NAMESPACE",  // Access as env.MY_NAMESPACE
    "id": "<production-uuid>",
    "preview_id": "<preview-uuid>"  // Optional: local dev
  }]
}

基本用法:

type Bindings = { MY_NAMESPACE: KVNamespace };

app.post('/set/:key', async (c) => {
  await c.env.MY_NAMESPACE.put(c.req.param('key'), await c.req.text());
  return c.json({ success: true });
});

app.get('/get/:key', async (c) => {
  const value = await c.env.MY_NAMESPACE.get(c.req.param('key'));
  return value ? c.json({ value }) : c.json({ error: 'Not found' }, 404);
});

KV API 参考

读取操作
// Get single key
const value = await env.MY_KV.get('key');  // string | null
const data = await env.MY_KV.get('key', { type: 'json' });  // object | null
const buffer = await env.MY_KV.get('key', { type: 'arrayBuffer' });
const stream = await env.MY_KV.get('key', { type: 'stream' });

// Get with cache (minimum 60s)
const value = await env.MY_KV.get('key', { cacheTtl: 300 });  // 5 min edge cache

// Bulk read (counts as 1 operation)
const values = await env.MY_KV.get(['key1', 'key2']);  // Map<string, string | null>

// With metadata
const { value, metadata } = await env.MY_KV.getWithMetadata('key');
const result = await env.MY_KV.getWithMetadata(['key1', 'key2']);  // Bulk with metadata
写入操作
// Basic write (max 1/second per key)
await env.MY_KV.put('key', 'value');
await env.MY_KV.put('user:123', JSON.stringify({ name: 'John' }));

// With expiration
await env.MY_KV.put('session', data, { expirationTtl: 3600 });  // 1 hour
await env.MY_KV.put('token', value, { expiration: Math.floor(Date.now()/1000) + 86400 });

// With metadata (max 1024 bytes)
await env.MY_KV.put('config', 'dark', {
  metadata: { updatedAt: Date.now(), version: 2 }
});

关键限制:

  • 键:最多 512 字节
  • 值:最多 25 MiB
  • 元数据:最多 1024 字节
  • 写入速率:每个键每秒 1 次(超出时返回 429 错误)
  • 过期时间:最短 60 秒
列表操作
// List with pagination
const result = await env.MY_KV.list({ prefix: 'user:', limit: 1000, cursor });
// result: { keys: [], list_complete: boolean, cursor?: string }

// CRITICAL: Always check list_complete, not keys.length === 0
let cursor: string | undefined;
do {
  const result = await env.MY_KV.list({ prefix: 'user:', cursor });
  processKeys(result.keys);
  cursor = result.list_complete ? undefined : result.cursor;
} while (cursor);
删除操作
// Delete single key
await env.MY_KV.delete('key');  // Always succeeds

// Bulk delete (CLI only, up to 10,000 keys)
// npx wrangler kv bulk delete --binding=MY_KV keys.json

高级模式

使用 CacheTtl 的缓存模式
async function getCachedData(kv: KVNamespace, key: string, fetchFn: () => Promise<any>, ttl = 300) {
  const cached = await kv.get(key, { type: 'json', cacheTtl: ttl });
  if (cached) return cached;

  const data = await fetchFn();
  await kv.put(key, JSON.stringify(data), { expirationTtl: ttl * 2 });
  return data;
}

指南:最短为 60 秒,适用于读取密集型工作负载(读写比为 100:1)

元数据优化
// Store small values (<1024 bytes) in metadata to avoid separate get() calls
await env.MY_KV.put('user:123', '', {
  metadata: { status: 'active', plan: 'pro', lastSeen: Date.now() }
});

// list() returns metadata automatically (no additional get() calls)
const users = await env.MY_KV.list({ prefix: 'user:' });
users.keys.forEach(({ name, metadata }) => console.log(name, metadata.status));
了解热键与冷键

KV 性能会因键的热度而异:

类型响应时间发生情况
热键6-8ms每个数据中心每分钟读取 2 次或以上
冷键100-300ms不常访问,从中央存储获取

2025 年 8 月之后的改进:

  • 所有 KV Worker 调用的 P90:<12ms(此前为 22ms)
  • 热读取速度最高提升 3 倍
  • 所有操作最多快 20ms

优化:使用键合并,让冷键也能受益于热键缓存:

// ❌ Bad: Many cold keys (300ms each)
await kv.put('user:123:name', 'John');
await kv.put('user:123:email', '[email protected]');
await kv.put('user:123:plan', 'pro');

// Each read of a cold key: ~100-300ms
const name = await kv.get('user:123:name');    // Cold
const email = await kv.get('user:123:email');  // Cold
const plan = await kv.get('user:123:plan');    // Cold

// ✅ Good: Single hot key (6-8ms)
await kv.put('user:123', JSON.stringify({
  name: 'John',
  email: '[email protected]',
  plan: 'pro'
}));

// Single read, cached as hot key: ~6-8ms
const user = JSON.parse(await kv.get('user:123'));

CacheTtl 有助于冷键:对于读取频率较低的数据,cacheTtl 可降低冷读取延迟。

权衡:合并更新时需要先读取,再修改并写入

分页辅助工具
async function* paginateKV(kv: KVNamespace, options: { prefix?: string } = {}) {
  let cursor: string | undefined;
  do {
    const result = await kv.list({ ...options, cursor });
    yield result.keys;
    cursor = result.list_complete ? undefined : result.cursor;
  } while (cursor);
}

// Usage
for await (const keys of paginateKV(env.MY_KV, { prefix: 'user:' })) {
  processKeys(keys);
}
使用指数退避重试速率限制错误
async function putWithRetry(kv: KVNamespace, key: string, value: string, opts?: KVPutOptions) {
  let attempts = 0, delay = 1000;
  while (attempts < 5) {
    try {
      await kv.put(key, value, opts);
      return;
    } catch (error) {
      if ((error as Error).message.includes('429')) {
        attempts++;
        if (attempts >= 5) throw new Error('Max retry attempts');
        await new Promise(r => setTimeout(r, delay));
        delay *= 2;  // Exponential backoff
      } else throw error;
    }
  }
}

了解最终一致性

KV 在 Cloudflare 的全球网络中具有最终一致性(2025 年 8 月重新设计:混合存储,p99 延迟 <5ms):

工作原理:

  1. 写入会立即在同一位置可见(在同一 POP 内可读到自己的写入)
  2. 其他位置会在约 60 秒内看到更新(或在 cacheTtl 指定的时间内)
  3. 在传播期间,缓存读取可能会返回过期数据

示例:

// Tokyo: Write
await env.MY_KV.put('counter', '1');
const value = await env.MY_KV.get('counter'); // "1" ✅ (same POP, RYOW)

// London (within 60s): May be stale ⚠️
const value2 = await env.MY_KV.get('counter'); // Might be old value

// After 60+ seconds: Consistent ✅

读后写一致性(RYOW)保证:自 2025 年 8 月重新设计以来,经由同一 Cloudflare 接入点路由的请求会立即看到自己的写入。不同 POP 之间的全局一致性仍需最多 60 秒。

时间戳缓解模式(适用于一致性要求较高的场景):

// Use timestamp in key structure to avoid consistency issues
const timestamp = Date.now();
await kv.put(`user:123:${timestamp}`, userData);

// Find latest using list with prefix
const result = await kv.list({ prefix: 'user:123:' });
const latestKey = result.keys.sort((a, b) =>
  parseInt(b.name.split(':')[2]) - parseInt(a.name.split(':')[2])
).at(0);

适用于 KV 的场景:读取密集型工作负载(读写比为 100:1)、配置、功能标志、缓存、用户偏好设置 不适用于 KV 的场景:金融交易、强一致性需求、每个键每秒写入超过 1 次、关键数据

**需要强一致性?**请使用 Durable Objects

来源:Workers KV 架构重新设计


Wrangler CLI 基础知识

# Create namespace
npx wrangler kv namespace create MY_NAMESPACE [--preview]

# Manage keys (add --remote flag to access production data)
npx wrangler kv key put --binding=MY_KV "key" "value" [--ttl=3600] [--metadata='{}']
npx wrangler kv key get --binding=MY_KV "key" [--remote]
npx wrangler kv key list --binding=MY_KV [--prefix="user:"] [--remote]
npx wrangler kv key delete --binding=MY_KV "key"

# Bulk operations (up to 10,000 keys)
npx wrangler kv bulk put --binding=MY_KV data.json
npx wrangler kv bulk delete --binding=MY_KV keys.json

重要:CLI 命令默认使用本地存储。添加 --remote 标志以访问生产环境/远程数据。


开发环境与生产环境

本地开发的远程绑定(Wrangler 4.37+)

在开发期间,将本地 Workers 连接到生产环境的 KV 命名空间:

wrangler.jsonc:

{
  "kv_namespaces": [{
    "binding": "MY_KV",
    "id": "production-uuid",
    "remote": true  // Connect to live KV
  }]
}

工作原理:

  • 本地 Worker 代码在本地执行(快速迭代)
  • KV 操作通过代理路由到生产环境的命名空间
  • 无需手动填充数据

优势:

  • 无需部署即可使用真实的生产数据进行测试
  • 本地代码执行速度快,同时可访问生产数据
  • 更快地获得反馈(无需经历部署-测试周期)

⚠️ 警告:写入操作会影响生产数据。请考虑使用设置了 remote: true 的暂存命名空间,而不是生产环境。

版本支持:

  • Wrangler 4.37.0+
  • @cloudflare/vite-plugin 1.13.0+
  • @cloudflare/vitest-pool-workers 0.9.0+

来源:远程绑定架构


限制与配额

功能免费计划付费计划
每日读取次数100,000无限制
每日写入次数(不同键)1,000无限制
每个键每秒写入次数11
每次 Worker 调用的操作数1,0001,000
每个账户的命名空间数1,0001,000
每个账户的存储空间1 GB无限制
键大小512 字节512 字节
元数据大小1024 字节1024 字节
值大小25 MiB25 MiB
最小 cacheTtl60 秒60 秒

重要提示:每个键每秒最多写入 1 次(超出会返回 429),批量操作计为 1 次操作;命名空间上限从 200 提高到 1,000(2025 年 1 月)


错误处理

1. 速率限制(429 Too Many Requests)

原因:向同一个键每秒写入超过 1 次 解决方案:使用带指数退避的重试(参见高级模式)

// ❌ Bad
await env.MY_KV.put('counter', '1');
await env.MY_KV.put('counter', '2'); // 429 error!

// ✅ Good
await putWithRetry(env.MY_KV, 'counter', '2');
2. 值过大

原因:值超过 25 MiB 解决方案:写入前验证大小

if (value.length > 25 * 1024 * 1024) throw new Error('Value exceeds 25 MiB');
3. 元数据过大

原因:序列化后的元数据超过 1024 字节 解决方案:验证序列化后的大小

const serialized = JSON.stringify(metadata);
if (serialized.length > 1024) throw new Error('Metadata exceeds 1024 bytes');
4. CacheTtl 无效

原因:cacheTtl <60 秒 解决方案:使用至少 60 秒

// ❌ Error
await env.MY_KV.get('key', { cacheTtl: 30 });

// ✅ Correct
await env.MY_KV.get('key', { cacheTtl: 60 });

重要规则

始终执行 ✅
  1. 读取多个键时使用批量操作(计为 1 次操作)
  2. 为频繁读取、很少更新的数据设置 cacheTtl(至少 60 秒)
  3. 频繁使用 list() 时,将小值(<1024 字节)存储在元数据中
  4. 分页时检查 list_complete,不要检查 keys.length === 0
  5. 写入操作使用带指数退避的重试逻辑
  6. 写入前验证大小(键 512B、值 25MiB、元数据 1KB)
  7. 合并相关键以获得更好的缓存性能
  8. 将 KV 用于读取密集型工作负载(理想的读写比为 100:1)
绝不执行 ❌
  1. 绝不要每秒向同一个键写入超过 1 次(会导致 429 速率限制错误)
  2. 绝不要假设会立即实现全局一致性(传播约需 60 秒)
  3. 绝不要将 KV 用于原子操作(改用 Durable Objects)
  4. 绝不要将 cacheTtl 设置为小于 60 秒(会失败)
  5. 绝不要将命名空间 ID 提交到公共仓库(使用环境变量)
  6. 绝不要在单次调用中超过 1000 次操作(使用批量操作)
  7. 绝不要依赖写入顺序(最终一致性意味着不作保证)
  8. 绝不要忘记处理 null 值(键不存在时,get() 会返回 null)

故障排除

问题 1:写入时出现“429 Too Many Requests”

原因:向同一个键每秒写入超过 1 次 解决方案:合并写入,或使用带指数退避的重试

// ❌ Bad: Rate limit
for (let i = 0; i < 10; i++) await kv.put('counter', String(i));

// ✅ Good: Single write
await kv.put('counter', '9');

// ✅ Good: Retry with backoff
await putWithRetry(kv, 'counter', String(i));
问题 2:写入后读取到过时数据

原因:最终一致性(传播约需 60 秒) 解决方案:接受读取到过时数据,使用 Durable Objects 实现强一致性,或实现应用级缓存失效

问题 3:“Operations limit exceeded”

原因:单次 Worker 调用中的 KV 操作超过 1000 次 解决方案:使用批量操作

// ❌ Bad: 5000 operations
for (const key of 5000keys) await kv.get(key);

// ✅ Good: 1 operation
const values = await kv.get(keys);  // Bulk read
问题 4:列表为空,但游标存在

原因:已删除或过期的键会产生必须遍历的“墓碑” 解决方案:始终检查 list_complete,而不是 keys.length

// ✅ Correct pagination
let cursor: string | undefined;
do {
  const result = await kv.list({ cursor });
  processKeys(result.keys);  // Even if empty
  cursor = result.list_complete ? undefined : result.cursor;
} while (cursor);

重要:使用 prefix 时,必须在所有分页调用中包含它:

// ❌ WRONG - Loses prefix on subsequent pages
let result = await kv.list({ prefix: 'user:' });
result = await kv.list({ cursor: result.cursor });  // Missing prefix!

// ✅ CORRECT - Include prefix on every call
let cursor: string | undefined;
do {
  const result = await kv.list({ prefix: 'user:', cursor });
  processKeys(result.keys);
  cursor = result.list_complete ? undefined : result.cursor;
} while (cursor);

来源:键列表文档

问题 5:wrangler types 不会为环境嵌套的 KV 绑定生成类型

原因:在环境配置(例如 [env.feature.kv_namespaces])中定义的 KV 命名空间不会包含在生成的 TypeScript 类型中 影响:KV 绑定失去 TypeScript 自动补全和类型检查 来源:GitHub 问题 #9709

配置示例:

# wrangler.toml
[env.feature]
name = "my-worker-feature"
[[env.feature.kv_namespaces]]
binding = "MY_STORAGE_FEATURE"
id = "xxxxxxxxxxxx"

运行 npx wrangler types 会为环境变量创建类型定义,但不会为 KV 命名空间绑定创建类型定义。

解决方法:

# Generate types for specific environment
npx wrangler types -e feature

或者,将 KV 命名空间定义在顶层,而不是嵌套在环境中:

# Top-level (types generated correctly)
[[kv_namespaces]]
binding = "MY_STORAGE"
id = "xxxxxxxxxxxx"

注意:运行时绑定仍可正常工作;这只会影响类型生成。

问题 6:wrangler kv key list 返回远程数据的空数组

原因:CLI 命令默认使用本地存储,而不是远程/生产 KV 影响:用户期望看到生产数据,却从本地存储中得到空数组 来源:GitHub 问题 #10395

解决方案:使用 --remote 标志访问生产/远程数据

# ❌ Shows local storage (likely empty)
npx wrangler kv key list --binding=MY_KV

# ✅ Shows remote/production data
npx wrangler kv key list --binding=MY_KV --remote

发生原因:按设计,wrangler dev 使用本地 KV 存储,以免影响生产数据。为保持一致性,CLI 命令也采用相同的默认设置。

适用范围:所有 wrangler kv key 命令(get、list、delete、put)


生产环境检查清单

  • 已配置环境专属命名空间(id 与 preview_id)
  • 命名空间 ID 存储在环境变量中(未硬编码)
  • 已为写入操作实现速率限制重试逻辑
  • 已为读取操作设置合适的 cacheTtl 值(至少 60s)
  • 已验证大小限制(键 512B、值 25MiB、元数据 1KB)
  • 在可能的情况下使用批量操作
  • 分页时检查 list_complete(而不是 keys.length)
  • 已处理空值
  • 已针对速率限制设置监控/告警

相关文档


最后更新:2026-01-20 软件包版本:[email protected], @cloudflare/[email protected] 变更:新增 6 项研究发现——热键/冷键性能模式、远程绑定(Wrangler 4.37+)、wrangler types 环境问题、CLI --remote 标志要求、RYOW 一致性详情、分页中的 prefix 持久性

评论

支持 Markdown