返回首页
⚙️ 后端 / 架构

Cloudflare Workers + D1 实战:边缘数据库全栈应用

Cloudflare Workers + D1 + R2 + Vectorize 是 2026 年最强边缘全栈组合:冷启动 < 5ms / 全球 300+ 节点 / 内置数据库 / 向量搜索。本文从入门到生产级 SaaS。

Cloudflare · Workers · D1 · 边缘数据库 · R2 · Vectorize · 全栈
📰

今日技术简讯

📰 技术简讯 · 2026-07-26

今日聚合 6 条热门技术内容(中文素材优先)。

🤖 AI / LLM

1. Cloudflare Workers AI 推出 DeepSeek-V3

2. Vectorize GA + Workers AI RAG

🎨 前端 / Web

3. Astro 6 推出 Cloudflare 适配

  • 链接https://astro.build/blog/6
  • 来源:Astro
  • 摘要:Astro 6 推出 Cloudflare 适配,SSR + 边缘 KV + D1 一键部署。

⚙️ 后端 / 架构

4. D1 推出 Read Replication

5. R2 + Workers 推出事件流

🚀 独立开发 / OPC

6. 即刻"边缘数据库"专题


数据来源:掘金 / InfoQ 中文 / 即刻 / 少数派 / HN 采集时间:2026-07-26 09:00 (UTC+8)

📝

今日深度文

Cloudflare Workers + D1 实战:边缘数据库全栈应用

一句话结论:Cloudflare Workers + D1 = 现代边缘全栈。冷启动 < 5ms / 全球 300+ 节点 / SQLite 兼容 / 无服务器费用。本文从入门到生产级 SaaS,含 5 个实战项目。

背景

Cloudflare 生态在 2026 年已完整:

  • Workers:边缘函数(V8 Isolates)
  • D1:边缘 SQLite(兼容 Prisma / Drizzle)
  • R2:S3 兼容对象存储
  • Vectorize:边缘向量数据库
  • KV:低延迟 KV 存储
  • Workers AI:边缘 AI 推理

"Workers + D1"组合 = 完整全栈

  • 无服务器费用(按请求付费)
  • 全球 300+ 节点
  • 冷启动 < 5ms
  • 内置数据库 / 存储 / AI / 向量搜索

6 大核心优势

1. 极致冷启动

// 冷启动对比
Node.js: 200ms
Deno: 5ms
Cloudflare Workers: < 5ms

Workers 用 V8 Isolates(轻量沙箱),无容器开销。

2. 全球部署

一次部署,全球 300+ 节点

美西 → 用户 50ms
欧洲 → 用户 30ms
亚洲 → 用户 20ms

3. 内置完整生态

服务 用途 价格
Workers 边缘函数 免费 100K/天
D1 SQLite 数据库 免费 5GB
R2 对象存储 免费 10GB
KV 低延迟 KV 免费 100K 读/天
Vectorize 向量搜索 免费 5M 维度
Workers AI AI 推理 免费 10K tokens/天

4. 强大工具链

# Wrangler CLI
$ npm install -g wrangler

$ wrangler init my-app
$ wrangler dev         # 本地开发
$ wrangler deploy      # 部署
$ wrangler d1 create   # 创建数据库
$ wrangler d1 execute  # 执行 SQL
$ wrangler tail        # 实时日志

5. 完整 TypeScript 支持

// wrangler.toml
name = "my-app"
main = "src/index.ts"
compatibility_date = "2026-07-01"

[[d1_databases]]
binding = "DB"
database_name = "my-db"
database_id = "xxx"

[[r2_buckets]]
binding = "STORAGE"
bucket_name = "my-bucket"

[[vectorize]]
binding = "VECTORIZE"
index_name = "my-index"

6. 多种部署方式

# 直接部署
$ wrangler deploy

# GitHub Actions 自动部署
# .github/workflows/deploy.yml

# Pages 部署
$ wrangler pages deploy ./dist

D1 实战

创建数据库

# 创建 D1
$ wrangler d1 create my-db
# 创建成功后会输出 database_id

# 配置 wrangler.toml
[[d1_databases]]
binding = "DB"
database_name = "my-db"
database_id = "xxx"

迁移

# 本地迁移
$ wrangler d1 execute my-db --local --file=./schema.sql

# 远程迁移
$ wrangler d1 execute my-db --remote --file=./schema.sql

schema.sql

CREATE TABLE users (
  id TEXT PRIMARY KEY,
  email TEXT UNIQUE NOT NULL,
  name TEXT NOT NULL,
  created_at INTEGER NOT NULL DEFAULT (unixepoch())
);

CREATE TABLE posts (
  id TEXT PRIMARY KEY,
  user_id TEXT NOT NULL,
  title TEXT NOT NULL,
  content TEXT NOT NULL,
  published INTEGER NOT NULL DEFAULT 0,
  created_at INTEGER NOT NULL DEFAULT (unixepoch()),
  FOREIGN KEY (user_id) REFERENCES users(id)
);

CREATE INDEX idx_posts_user_id ON posts(user_id);
CREATE INDEX idx_posts_created_at ON posts(created_at DESC);

CRUD API

// src/index.ts
export interface Env {
  DB: D1Database;
}

export default {
  async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url);
    
    // CORS
    if (request.method === 'OPTIONS') {
      return new Response(null, {
        headers: {
          'Access-Control-Allow-Origin': '*',
          'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
          'Access-Control-Allow-Headers': 'Content-Type',
        },
      });
    }
    
    // 列表
    if (url.pathname === '/api/posts' && request.method === 'GET') {
      const { results } = await env.DB.prepare(
        'SELECT * FROM posts WHERE published = ? ORDER BY created_at DESC LIMIT 20'
      ).bind(1).all();
      
      return Response.json(results);
    }
    
    // 创建
    if (url.pathname === '/api/posts' && request.method === 'POST') {
      const { title, content, userId } = await request.json();
      const id = crypto.randomUUID();
      
      await env.DB.prepare(
        'INSERT INTO posts (id, user_id, title, content) VALUES (?, ?, ?, ?)'
      ).bind(id, userId, title, content).run();
      
      return Response.json({ id }, { status: 201 });
    }
    
    // 详情
    if (url.pathname.startsWith('/api/posts/') && request.method === 'GET') {
      const id = url.pathname.split('/').pop();
      const post = await env.DB.prepare(
        'SELECT * FROM posts WHERE id = ?'
      ).bind(id).first();
      
      if (!post) return new Response('Not found', { status: 404 });
      return Response.json(post);
    }
    
    return new Response('Not found', { status: 404 });
  },
};

使用 Drizzle(类型安全)

// src/db.ts
import { drizzle } from 'drizzle-orm/d1';
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
import { eq, desc } from 'drizzle-orm';

export const users = sqliteTable('users', {
  id: text('id').primaryKey(),
  email: text('email').notNull().unique(),
  name: text('name').notNull(),
  createdAt: integer('created_at').notNull(),
});

export const posts = sqliteTable('posts', {
  id: text('id').primaryKey(),
  userId: text('user_id').notNull(),
  title: text('title').notNull(),
  content: text('content').notNull(),
  published: integer('published').notNull().default(0),
  createdAt: integer('created_at').notNull(),
});

export function getDb(d1: D1Database) {
  return drizzle(d1, { schema: { users, posts } });
}

// src/api.ts
import { getDb, posts } from './db';

export default {
  async fetch(request: Request, env: Env) {
    const db = getDb(env.DB);
    
    const list = await db.select().from(posts)
      .where(eq(posts.published, 1))
      .orderBy(desc(posts.createdAt))
      .limit(20);
    
    return Response.json(list);
  },
};

4 个实战项目

项目 1:博客系统

export default {
  async fetch(request: Request, env: Env) {
    const url = new URL(request.url);
    
    if (url.pathname === '/api/posts' && request.method === 'GET') {
      const { results } = await env.DB.prepare(`
        SELECT p.*, u.name as author_name
        FROM posts p
        JOIN users u ON p.user_id = u.id
        WHERE p.published = 1
        ORDER BY p.created_at DESC
        LIMIT 20
      `).all();
      return Response.json(results);
    }
    
    // ...
  },
};

项目 2:短链服务

export default {
  async fetch(request: Request, env: Env) {
    const url = new URL(request.url);
    
    // 创建短链
    if (url.pathname === '/api/shorten' && request.method === 'POST') {
      const { target } = await request.json();
      const code = Math.random().toString(36).slice(2, 8);
      
      await env.DB.prepare(
        'INSERT INTO short_links (code, target, created_at) VALUES (?, ?, ?)'
      ).bind(code, target, Date.now()).run();
      
      return Response.json({ code, short: `${url.origin}/${code}` });
    }
    
    // 访问短链
    const code = url.pathname.slice(1);
    if (code && code !== 'api') {
      const link = await env.DB.prepare(
        'SELECT target FROM short_links WHERE code = ?'
      ).bind(code).first();
      
      if (link) {
        // 统计点击
        await env.DB.prepare(
          'UPDATE short_links SET clicks = clicks + 1 WHERE code = ?'
        ).bind(code).run();
        
        return Response.redirect(link.target as string, 302);
      }
    }
    
    return new Response('Not found', { status: 404 });
  },
};

项目 3:RAG 应用(与 7/15 关联)

export interface Env {
  DB: D1Database;
  VECTORIZE: VectorizeIndex;
  AI: Ai;
}

export default {
  async fetch(request: Request, env: Env) {
    const url = new URL(request.url);
    
    // 上传文档
    if (url.pathname === '/api/documents' && request.method === 'POST') {
      const { title, content } = await request.json();
      const id = crypto.randomUUID();
      
      // 生成 embedding(Workers AI)
      const { response } = await env.AI.run(
        '@cf/baai/bge-base-en-v1.5',
        { text: content }
      ) as { response: number[] };
      
      // 存 D1
      await env.DB.prepare(
        'INSERT INTO documents (id, title, content) VALUES (?, ?, ?)'
      ).bind(id, title, content).run();
      
      // 存 Vectorize
      await env.VECTORIZE.upsert([{
        id,
        values: response,
        metadata: { title },
      }]);
      
      return Response.json({ id });
    }
    
    // 搜索
    if (url.pathname === '/api/search' && request.method === 'POST') {
      const { query } = await request.json();
      
      // Embedding
      const { response } = await env.AI.run(
        '@cf/baai/bge-base-en-v1.5',
        { text: query }
      ) as { response: number[] };
      
      // 向量搜索
      const results = await env.VECTORIZE.query(response, {
        topK: 5,
        returnMetadata: 'all',
      });
      
      return Response.json(results);
    }
    
    return new Response('Not found', { status: 404 });
  },
};

项目 4:R2 文件上传

export interface Env {
  STORAGE: R2Bucket;
}

export default {
  async fetch(request: Request, env: Env) {
    const url = new URL(request.url);
    
    // 上传
    if (url.pathname === '/api/upload' && request.method === 'POST') {
      const formData = await request.formData();
      const file = formData.get('file') as File;
      const key = `${Date.now()}-${file.name}`;
      
      await env.STORAGE.put(key, file.stream(), {
        httpMetadata: { contentType: file.type },
        customMetadata: { uploadedBy: 'anonymous' },
      });
      
      return Response.json({ key, url: `/api/files/${key}` });
    }
    
    // 下载
    if (url.pathname.startsWith('/api/files/')) {
      const key = url.pathname.split('/').pop()!;
      const object = await env.STORAGE.get(key);
      
      if (!object) return new Response('Not found', { status: 404 });
      
      return new Response(object.body, {
        headers: {
          'content-type': object.httpMetadata?.contentType || 'application/octet-stream',
          'cache-control': 'public, max-age=31536000',
        },
      });
    }
    
    return new Response('Not found', { status: 404 });
  },
};

5 个常见坑

坑 1:D1 单数据库限制

// ❌ 单数据库最大 10GB
// 解决方案:分库 / 归档旧数据

// ❌ 写延迟 100ms(vs PostgreSQL 10ms)
// 解决方案:用 KV 缓存热点数据

坑 2:CPU 时间限制

Workers 免费版:10ms CPU 时间
Workers 付费版:50ms(默认)/ 30s(最长)

❌ 同步长任务
✅ 异步 + 后台任务(Queues / Cron Triggers)

坑 3:本地开发差异

// ❌ 有些 API 本地不支持
// --local 模式 vs --remote 模式

// ✅ 部署前用 --remote 测试
$ wrangler dev --remote

坑 4:冷启动数据库连接

// ❌ 每次请求都创建连接
const conn = new Connection();

D1 自动管理连接(每次请求一个新连接)

坑 5:状态不能跨请求

// ❌ 用全局变量缓存
let cache = {};  // ❌ Worker 可能被销毁

// ✅ 用 D1 / KV / R2 / Vectorize
await env.KV.get('key');

与之前内容的关系

7/17 Rust + Axum         → 后端框架
7/18 Vercel + Cloudflare → 部署(含 CF)
7/19 Docker              → 容器化
7/21 PostgreSQL          → 数据库
7/25 Deno 2 + Hono       → 运行时
7/26 Workers + D1        → 边缘全栈  ← 今天
→ "框架 → 部署 → 数据库 → 运行时 → 边缘"

7 天落地路径

Day 1:注册 Cloudflare

- 注册账号
- 安装 wrangler
- wrangler login

Day 2:第一个 Worker

$ wrangler init my-app
$ cd my-app
$ wrangler dev

Day 3:D1 数据库

$ wrangler d1 create my-db
# 配置 wrangler.toml
$ wrangler d1 execute my-db --local --file=./schema.sql

Day 4:CRUD API

// 列表 / 详情 / 创建 / 更新 / 删除

Day 5:前端集成

// React / Vue / Astro

Day 6:高级功能

// R2 文件 / Vectorize 向量搜索 / Workers AI

Day 7:部署 + 监控

$ wrangler deploy
$ wrangler tail  # 实时日志

我的看法

Cloudflare Workers + D1 是 2026 年独立开发者的"瑞士军刀"

  1. 零服务器费用:免费额度足够个人项目
  2. 极致性能:全球 300+ 节点 / 冷启动 < 5ms
  3. 完整生态:DB / Storage / AI / Vector / KV
  4. 简单部署wrangler deploy 一行命令
  5. 类型安全:完整 TypeScript

对独立开发者的建议:

  • 小项目首选:博客 / Landing Page / SaaS MVP
  • 中等项目:完整全栈应用
  • 大项目:搭配专用数据库(Neon / Supabase)

参考


本文基于 Cloudflare Workers 2026 年 7 月最新版本。

📚 同主题文章

⚙️ 后端 / 架构 分类更多