ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Cloudflare D1 数据库完全指南:从零搭建 Workers 的无服务器 SQLite 存储层

Cloudflare D1 数据库完全指南:从零搭建 Workers 的无服务器 SQLite 存储层 【免费下载链接】autoskillsOne command. Your entire AI skill stack. Installed.项目地址https://gitcode.com/gh_mirrors/au/autoskills点击查看免费下载D1 是 Cloudflare 提供的托管式无服务器 SQLite 数据库专为水平扩展的多数据库架构而设计可直接在 Worker 运行时与 HTTP API 中访问按查询与存储量计费。本文以 autoskills 仓库中 cloudflare-deploy 技能包内 D1 文档为主线系统讲解从wrangler d1建库、wrangler.jsonc绑定配置、迁移管理、查询 API、批量事务、Sessions 长会话、读副本到生产级查询模式与排障的全链路实战方案读完后你可以独立为 Worker 应用搭建一套可上生产的 D1 数据层。什么是 D1为每租户一库而生的无服务器数据库D1 是 Cloudflare 官方托管的 serverless SQLite 数据库其设计目标与一个大而全的中心数据库的传统思路截然不同——它的架构哲学是为 per-user、per-tenant、per-entity 的数据库模式做优化即每个用户、每个租户或每个实体独立拥有自己的小型数据库通过水平扩展horizontal scale-out获得整体容量与隔离性。从能力清单看D1 具备以下核心特征见 d1/README.md完整的 SQLite SQL 语义与兼容性无需学习新的查询语言标准 SQL 直接可用内置灾难恢复通过 Time Travel 提供时间点恢复point-in-time recovery免费版保留 7 天付费版保留 30 天水平扩展架构单个数据库上限 10 GB免费版 500 MB容量靠拆库而不是扩单库解决双重访问途径既可以在 Worker 内通过env.DB绑定直接访问也可以通过 Cloudflare REST API 从任何外部服务访问透明定价只按查询次数与存储量计费不按连接数或预留实例计费。在 cloudflare-deploy/SKILL.md 的存储决策树中D1 被定位为关系型 SQL 的首选方案d1/负责 SQLite 场景而外部 Postgres/MySQL 则交给 hyperdrive 做连接池加速KV 负责键值缓存、R2 负责对象存储、Queues 负责异步消息队列。这意味着选型时只要出现关系模型、需要 JOIN、需要事务的需求D1 就是 Worker 生态内的默认答案。快速开始三条命令跑通 D1新建数据库、执行迁移、本地开发只需三步命令均通过 Wrangler CLI 完成# 创建数据库会在 Cloudflare 侧生成 database_id wrangler d1 create database-name # 将迁移应用到远端生产数据库 wrangler d1 migrations apply db-name --remote # 启动本地开发服务器自动连接本地 SQLite 副本 wrangler dev三个命令分别对应 D1 生命周期的三个阶段建库远程资源创建、迁移表结构落地、本地开发联调验证。生产部署前建议先通过 wrangler/auth.md 完成认证——本地交互开发用wrangler login一次性 OAuthCI/CD 或无人值守环境则设置CLOUDFLARE_API_TOKEN环境变量创建令牌时可选用 Edit Cloudflare Workers 模板覆盖 Workers、Pages、KV、D1、R2 权限并用npx wrangler whoami验证账号与令牌作用域。本地开发的数据持久化wrangler dev默认把本地数据库落盘为一个 SQLite 文件。通过--persist-to参数可以指定持久化目录使本地数据在多次重启之间保留wrangler dev --persist-to./.wrangler/state # 本地数据库文件位于.wrangler/state/v3/d1/database-id.sqlite sqlite3 .wrangler/state/v3/d1/database-id.sqlite # 直接查看本地数据注意一个关键差异本地开发默认套用免费版额度限制如 500 MB 库大小、1,000 条批量上限且本地是单机 SQLite 文件、生产是分布式 D1两者在性能与限额上并不完全等价——这也是 gotchas.md 中反复强调上线前务必用--remote在真实环境验证迁移的原因。wrangler.jsonc 配置绑定、读副本与多库在wrangler.jsonc中通过d1_databases数组声明数据库绑定每个绑定对应 Worker 环境对象上的一个变量名详见 configuration.md{ name: your-worker-name, main: src/index.ts, compatibility_date: 2025-01-01, // 新项目使用当前日期 d1_databases: [ { binding: DB, // 环境变量名代码中访问 env.DB database_name: your-db-name, // 人类可读的数据库名 database_id: your-database-id, // 从 Dashboard/CLI 获取的 UUID migrations_dir: migrations // 可选默认是 migrations }, // 读副本绑定仅付费套餐同一个 database_id不同的 binding { binding: DB_REPLICA, database_name: your-db-name, database_id: your-database-id }, // 多数据库一个 Worker 可以挂多个 D1 { binding: ANALYTICS_DB, database_name: analytics-db, database_id: yyy-yyy-yyy } ] }要点解析binding是代码侧访问入口Worker 的fetch处理器会收到env.DB、env.ANALYTICS_DB等对应对象读副本Read Replica在付费套餐下可用配置方式是同一database_id、不同binding代码中把读流量指向DB_REPLICA、写流量留在DB多库共存是 D1 架构哲学的落地形式——把 analytics 等不同域的数据拆到独立数据库互不干扰。对应的 TypeScript 类型声明与最小 Worker 示例interface Env { DB: D1Database; ANALYTICS_DB?: D1Database; } export default { async fetch(request: Request, env: Env, ctx: ExecutionContext): PromiseResponse { const result await env.DB.prepare(SELECT * FROM users).all(); return Response.json(result.results); } }迁移管理从建表到生产发布迁移文件结构迁移采用按序号排列的 SQL 文件Wrangler 会自动创建d1_migrations表来跟踪已应用的迁移migrations/0001_initial_schema.sql migrations/0002_add_posts.sql一份典型的初始迁移建表 索引 外键-- migrations/0001_initial_schema.sql CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT, email TEXT UNIQUE NOT NULL, name TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP, updated_at TEXT DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_users_email ON users(email); CREATE TABLE IF NOT EXISTS posts ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, title TEXT NOT NULL, content TEXT, published BOOLEAN DEFAULT 0, created_at TEXT DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE INDEX idx_posts_user_id ON posts(user_id); CREATE INDEX idx_posts_published ON posts(published);迁移命令全景# 生成新的迁移文件 wrangler d1 migrations create db-name add_users_table # 生成结果migrations/0001_add_users_table.sql # 应用迁移 wrangler d1 migrations apply db-name --local # 应用到本地数据库 wrangler d1 migrations apply db-name --remote # 应用到生产数据库 # 查看已应用的迁移 wrangler d1 migrations list db-name --remote # 直接执行 SQL绕过迁移追踪适合一次性操作 wrangler d1 execute db-name --remote --commandSELECT * FROM users wrangler d1 execute db-name --local --file./schema.sql最常见的生产事故之一就是迁移只应用到了本地而忘了--remote见 gotchas.md表现为生产环境报no such table。请把wrangler d1 migrations apply db-name --remote固化为上线流程的固定步骤。索引策略SQLite 查询优化的核心在索引。文档给出了从单列到复合、覆盖、部分索引的完整梯度-- 高频查询列建单列索引 CREATE INDEX idx_users_email ON users(email); -- 多列条件查询建复合索引注意列顺序与查询条件匹配 CREATE INDEX idx_posts_user_published ON posts(user_id, published); -- 覆盖索引把要 SELECT 的列也放进索引避免回表 CREATE INDEX idx_users_email_name ON users(email, name); -- 部分索引只为被过滤的行子集建索引节省空间 CREATE INDEX idx_active_users ON users(email) WHERE active 1; -- 验证查询是否命中索引 EXPLAIN QUERY PLAN SELECT * FROM users WHERE email ?;当查询变慢时先EXPLAIN QUERY PLAN看是否走了索引而不是盲目堆索引。查询 API预备语句与四种执行方法预备语句是安全底线D1 的查询 API 要求必须使用预备语句Prepared Statementsbind()严禁字符串插值——后者会把用户输入直接拼进 SQL构成注入漏洞// ❌ 错误字符串插值存在 SQL 注入风险 const result await env.DB.prepare(SELECT * FROM users WHERE id ${userId}).all(); // ✅ 正确预备语句 bind() const result await env.DB.prepare(SELECT * FROM users WHERE id ?).bind(userId).all(); // 多参数 const result await env.DB.prepare(SELECT * FROM users WHERE email ? AND active ?).bind(email, true).all();四种执行方法api.md方法适用场景返回内容.all()SELECT 查询全部行{ results, success, meta }results为行对象数组.first()取第一行或 null行对象或 null.first(columnName)取单列值string \| number \| null.run()INSERT/UPDATE/DELETE不返回行数据meta含last_row_id、changes.raw()大数据集高效读取二维数组如[[1, Alice], [2, Bob]]// .all() - 返回所有行meta 带执行时长与读写行数 const { results, success, meta } await env.DB.prepare(SELECT * FROM users WHERE active ?).bind(true).all(); // meta: { duration: number, rows_read: number, rows_written: number } // .first() - 第一行或 null const user await env.DB.prepare(SELECT * FROM users WHERE id ?).bind(userId).first(); // .first(columnName) - 单个列值 const email await env.DB.prepare(SELECT email FROM users WHERE id ?).bind(userId).first(email); // .run() - 写操作meta 含 last_row_id 与 changes const result await env.DB.prepare(UPDATE users SET last_login ? WHERE id ?).bind(Date.now(), userId).run(); // .raw() - 数组套数组适合大批量结果的序列化 const rawResults await env.DB.prepare(SELECT id, name FROM users).raw();条件查询的常规写法动态拼接 WHERE 时条件与参数必须同步累积始终用?占位符async function searchUsers(filters: { name?: string; email?: string; active?: boolean }, env: Env) { const conditions: string[] [], params: (string | number | boolean | null)[] []; if (filters.name) { conditions.push(name LIKE ?); params.push(%${filters.name}%); } if (filters.email) { conditions.push(email ?); params.push(filters.email); } if (filters.active ! undefined) { conditions.push(active ?); params.push(filters.active ? 1 : 0); } const whereClause conditions.length 0 ? WHERE ${conditions.join( AND )} : ; return await env.DB.prepare(SELECT * FROM users ${whereClause}).bind(...params).all(); }批量操作与事务一次往返、原子提交batch()把多条语句打包在一次网络往返中执行并作为原子事务处理——要么全部成功要么全部失败// 多条独立查询合并为一次往返 const results await env.DB.batch([ env.DB.prepare(SELECT * FROM users WHERE id ?).bind(1), env.DB.prepare(SELECT * FROM posts WHERE author_id ?).bind(1), env.DB.prepare(UPDATE users SET last_access ? WHERE id ?).bind(Date.now(), 1) ]); // results 是数组[result1, result2, result3] // 同一条预备语句复用绑定不同参数 const userIds [1, 2, 3]; const stmt env.DB.prepare(SELECT * FROM users WHERE id ?); const results await env.DB.batch(userIds.map(id stmt.bind(id)));典型事务场景——账户转账4 条语句原子执行const results await env.DB.batch([ env.DB.prepare(INSERT INTO accounts (id, balance) VALUES (?, ?)).bind(1, 100), env.DB.prepare(INSERT INTO accounts (id, balance) VALUES (?, ?)).bind(2, 200), env.DB.prepare(UPDATE accounts SET balance balance - ? WHERE id ?).bind(50, 1), env.DB.prepare(UPDATE accounts SET balance balance ? WHERE id ?).bind(50, 2) ]);批量插入同样推荐batch()预先编译一条 INSERT 语句映射每个参数组再批量提交。性能进阶Sessions API 与读副本付费套餐Sessions API突破 30 秒查询超时普通查询有 30 秒超时上限而Sessions 提供最长 15 分钟的长期会话专为迁移、ANALYZE、大规模索引创建、批量数据转换这类重型操作设计// 创建 10 分钟会话timeout 范围 1-900 秒 const session env.DB.withSession({ timeout: 600 }); try { await session.prepare(CREATE INDEX idx_heavy ON large_table(column)).run(); await session.prepare(ANALYZE).run(); } finally { session.close(); // 关键必须关闭以释放资源 }finally中的session.close()是硬性要求——忘记关闭会造成资源泄漏这是 gotchas.md 专门列出的生产级问题。Sessions 也支持session.batch()例如分批游标式遍历大表做数据变换async function transformLargeDataset(env: Env) { const session env.DB.withSession({ timeout: 900 }); // 15 分钟上限 try { const BATCH_SIZE 1000; let offset 0; while (true) { const rows await session.prepare(SELECT id, data FROM legacy LIMIT ? OFFSET ?).bind(BATCH_SIZE, offset).all(); if (rows.results.length 0) break; const updates rows.results.map(row session.prepare(UPDATE legacy SET new_data ? WHERE id ?).bind(transform(row.data), row.id) ); await session.batch(updates); offset BATCH_SIZE; } } finally { session.close(); } }读副本就近读、主库写读副本把读流量路由到最近的副本以降低延迟写永远走主库。需要注意复制延迟约 100ms–2s因此读后写read-after-write必须回主库以保证一致性interface Env { DB: D1Database; // 主库写 DB_REPLICA: D1Database; // 副本读 } // 读走副本 const user await env.DB_REPLICA.prepare(SELECT * FROM users WHERE id ?).bind(userId).first(); // 写走主库 await env.DB.prepare(UPDATE users SET last_login ? WHERE id ?).bind(Date.now(), userId).run(); // 读后写回主库读取避免读到滞后副本 await env.DB.prepare(INSERT INTO posts (title) VALUES (?)).bind(title).run(); const post await env.DB.prepare(SELECT * FROM posts WHERE title ?).bind(title).first(); // 主库在 patterns.md 中给出的使用准则很清晰副本适用分析仪表盘、搜索结果、公开查询等允许最终一致性的场景主库适用读后写、金融类事务、认证鉴权等强一致性场景。平台限额与计费设计前的硬约束D1 的限额是架构设计的起点。免费版与付费版的差异d1/README.md 与 gotchas.md 两处表格略有差异综合如下限额项免费版付费版备注单库大小500 MB10 GB付费版也建议按多库拆分设计单行大小1 MB1 MB大文件放 R2D1 只存 URL/Key查询超时30 秒30 秒Sessions 最长 900 秒重型操作用 Sessions批量语句数1,000 条10,000 条超限需分块提交Time Travel 保留7 天30 天时间点恢复窗口读副本不可用可用付费附加项降低读延迟Sessions API不可用可用最长 15 分钟迁移/重型操作并发请求约 10,000/min更高特殊需求联系支持计费规则付费套餐每库每月 $5超出免费额度部分 每 1K 次读 $0.001 每 1M 次写 $1 存储每 GB/月 $0.75。这些数字直接决定工程决策单库超过 10 GB 就应水平拆库批处理超过 1,000/10,000 条就要分块二进制大对象绝不该进 D1。CLI 命令速查手册数据库生命周期管理wrangler d1 create db-name # 创建数据库 wrangler d1 list # 列出账号下所有 D1 wrangler d1 delete db-name # 删除数据库迁移wrangler d1 migrations create db-name migration-name # 生成迁移文件 wrangler d1 migrations apply db-name --remote # 应用到生产 wrangler d1 migrations apply db-name --local # 应用到本地 wrangler d1 migrations list db-name --remote # 查看已应用迁移直接执行 SQLwrangler d1 execute db-name --remote --commandSELECT * FROM users wrangler d1 execute db-name --local --file./schema.sql备份、导入导出与时间点恢复# 全量导出含 schema wrangler d1 export db-name --remote --output./backup.sql # 仅导出数据不含 schema wrangler d1 export db-name --remote --no-schema --output./data.sql # 时间点恢复把数据库恢复到指定时刻 wrangler d1 time-travel restore db-name --timestamp2024-01-15T14:30:00Z # 查看可用恢复点免费 7 天付费 30 天 wrangler d1 time-travel info db-name # 导入 SQL 文件 wrangler d1 execute db-name --remote --file./backup.sql导出/导入的已知限制configuration.mdBLOB 数据可能无法正确导出二进制文件请存 R2超大导出1GB可能超时需要分块导入不是原子的需要事务性导入时应在 Worker 内用batch()实现。此外导出时外键约束默认关闭以保证导入兼容性。生产级模式分页、缓存、多租户与 KV 组合分页COUNT LIMIT/OFFSET 一次往返async function getUsers({ page, pageSize }: { page: number; pageSize: number }, env: Env) { const offset (page - 1) * pageSize; const [countResult, dataResult] await env.DB.batch([ env.DB.prepare(SELECT COUNT(*) as total FROM users), env.DB.prepare(SELECT * FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?).bind(pageSize, offset) ]); return { data: dataResult.results, total: countResult.results[0].total, page, pageSize, totalPages: Math.ceil(countResult.results[0].total / pageSize) }; }批量插入async function bulkInsertUsers(users: Array{ name: string; email: string }, env: Env) { const stmt env.DB.prepare(INSERT INTO users (name, email) VALUES (?, ?)); const batch users.map(user stmt.bind(user.name, user.email)); return await env.DB.batch(batch); }用 KV 做查询缓存D1 负责强一致存储KV 负责热路径缓存两者互补async function getCachedUser(userId: number, env: { DB: D1Database; CACHE: KVNamespace }) { const cacheKey user:${userId}; const cached await env.CACHE?.get(cacheKey, json); if (cached) return cached; const user await env.DB.prepare(SELECT * FROM users WHERE id ?).bind(userId).first(); if (user) await env.CACHE?.put(cacheKey, JSON.stringify(user), { expirationTtl: 300 }); return user; }查询优化向 N1 说不// ❌ N1循环内逐条查询多次网络往返 for (const post of posts) { const author await env.DB.prepare(SELECT * FROM users WHERE id ?).bind(post.user_id).first(); } // ✅ 用 JOIN 一次取回关联数据 const postsWithAuthors await env.DB.prepare( SELECT posts.*, users.name as author_name FROM posts JOIN users ON posts.user_id users.id ).all(); // ✅ 多个独立查询用 batch() 合并往返 const [user, posts, comments] await env.DB.batch([ env.DB.prepare(SELECT * FROM users WHERE id ?).bind(userId), env.DB.prepare(SELECT * FROM posts WHERE user_id ?).bind(userId), env.DB.prepare(SELECT * FROM comments WHERE user_id ?).bind(userId) ]);多租户 SaaS每租户一库这正是 D1水平扩展 每实体一库哲学的典型落地——租户 ID 直接作为绑定键export default { async fetch(request: Request, env: { [key: TENANT_${string}]: D1Database }) { const tenantId request.headers.get(X-Tenant-ID); const data await env[TENANT_${tenantId}].prepare(SELECT * FROM records).all(); return Response.json(data.results); } }会话存储与事件分析// 建会话过期时间用 ISO 8601 文本存储 async function createSession(userId: number, token: string, env: Env) { const expiresAt new Date(Date.now() 7 * 24 * 60 * 60 * 1000).toISOString(); return await env.DB.prepare(INSERT INTO sessions (user_id, token, expires_at) VALUES (?, ?, ?)).bind(userId, token, expiresAt).run(); } // 校验会话JOIN 用户表 过期时间判断 async function validateSession(token: string, env: Env) { return await env.DB.prepare(SELECT s.*, u.email FROM sessions s JOIN users u ON s.user_id u.id WHERE s.token ? AND s.expires_at CURRENT_TIMESTAMP).bind(token).first(); } // 事件埋点metadata 用 JSON 字符串 async function logEvent(event: { type: string; userId?: number; metadata: object }, env: Env) { return await env.DB.prepare(INSERT INTO events (type, user_id, metadata) VALUES (?, ?, ?)).bind(event.type, event.userId || null, JSON.stringify(event.metadata)).run(); }错误处理与常见坑Gotchas错误处理模板async function getUser(userId: number, env: Env): PromiseResponse { try { const result await env.DB.prepare(SELECT * FROM users WHERE id ?).bind(userId).all(); if (!result.success) return new Response(Database error, { status: 500 }); if (result.results.length 0) return new Response(User not found, { status: 404 }); return Response.json(result.results[0]); } catch (error) { return new Response(Internal error, { status: 500 }); } } // 唯一约束冲突 → 409 Conflict try { await env.DB.prepare(INSERT INTO users (email, name) VALUES (?, ?)).bind(email, name).run(); } catch (error) { if (error.message?.includes(UNIQUE constraint failed)) return new Response(Email exists, { status: 409 }); throw error; }高频问题与解决方案速查gotchas.md错误/症状原因解决方案SQL 注入漏洞字符串插值而非预备语句一律prepare(...).bind(...)no such table迁移未跑或绑定名不匹配migrations apply --remote核对 wrangler.jsonc 绑定UNIQUE constraint failed插入重复唯一值捕获异常返回 409查询超时30s大查询/无索引拆小查询、加索引、减小数据集N1 查询循环内逐条查询JOIN 或batch()缺索引全表扫描EXPLAIN QUERY PLAN检查后建索引布尔类型问题SQLite 只有 INTEGER(0/1)bind 1/0 而非 true/false日期类型问题SQLite 无原生 DATE/TIME用 TEXT(ISO 8601) 或 INTEGER(时间戳)批量超限超过 1,000/10,000 条分块循环提交会话未关闭忘记session.close()try/finally 包裹副本读到旧数据复制延迟 100ms-2s读后写走主库迁移只到本地忘加--remote生产环境固定带--remote外键约束失败父行不存在/先删父行PRAGMA foreign_keys ON; schema 中ON DELETE CASCADEBLOB 导出损坏D1 导出对 BLOB 处理不佳二进制放 R2D1 只存 URL/Key库容量逼近上限单库数据过多每租户/每用户拆库、归档旧数据、或升级付费版本地与生产行为不一致本地单机 SQLite vs 生产分布式 D1上线前用--remote验证外键的一个重要细节SQLite 默认不强制外键需要执行PRAGMA foreign_keys ON;开启约束每次连接/会话生效。通过 REST API 在 Worker 之外访问 D1如果需要在非 Worker 环境服务端脚本、CI/CD、管理工具访问 D1可以直接调用 Cloudflare API 的 query 端点// 单条查询 const response await fetch( https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/d1/database/${DATABASE_ID}/query, { method: POST, headers: { Authorization: Bearer ${CLOUDFLARE_API_TOKEN}, Content-Type: application/json }, body: JSON.stringify({ sql: SELECT * FROM users WHERE id ?, params: [userId] }) } ); const { result, success, errors } await response.json(); // result: [{ results: [...], success: true, meta: {...} }] // 批量查询body 传数组即可 const response await fetch( https://api.cloudflare.com/client/v4/accounts/${ACCOUNT_ID}/d1/database/${DATABASE_ID}/query, { method: POST, headers: { Authorization: Bearer ${CLOUDFLARE_API_TOKEN}, Content-Type: application/json }, body: JSON.stringify([ { sql: SELECT * FROM users WHERE id ?, params: [1] }, { sql: SELECT * FROM posts WHERE author_id ?, params: [1] } ]) } );这为外部服务 D1的集成比如离线脚本跑迁移、后台任务写分析数据提供了官方通道。测试与调试用 Vitest unstable_dev 做集成测试import { unstable_dev } from wrangler; describe(D1, () { let worker: AwaitedReturnTypetypeof unstable_dev; beforeAll(async () { worker await unstable_dev(src/index.ts); }); afterAll(async () { await worker.stop(); }); it(queries users, async () { expect((await worker.fetch(/users)).status).toBe(200); }); });查询性能诊断// 查看单次查询耗时 const result await env.DB.prepare(SELECT * FROM users).all(); console.log(Duration:, result.meta.duration, ms); // 查询计划分析确认索引是否生效 const plan await env.DB.prepare(EXPLAIN QUERY PLAN SELECT * FROM users WHERE email ?).bind(email).all();# 直接检查本地 SQLite 文件 sqlite3 .wrangler/state/v3/d1/database-id.sqlite .tables; .schema users; PRAGMA table_info(users);ORM 集成DrizzleD1 同样支持 ORM 工作流。以 Drizzle 为例配置、schema 与 Worker 集成三件套configuration.md// drizzle.config.ts export default { schema: ./src/schema.ts, out: ./migrations, dialect: sqlite, driver: d1-http, dbCredentials: { accountId: process.env.CLOUDFLARE_ACCOUNT_ID!, databaseId: process.env.D1_DATABASE_ID!, token: process.env.CLOUDFLARE_API_TOKEN! } } satisfies Config; // src/schema.ts import { sqliteTable, text, integer } from drizzle-orm/sqlite-core; export const users sqliteTable(users, { id: integer(id).primaryKey({ autoIncrement: true }), email: text(email).notNull().unique(), name: text(name).notNull() }); // worker.ts直接以 env.DB 驱动 drizzle import { drizzle } from drizzle-orm/d1; import { users } from ./schema; export default { async fetch(request: Request, env: Env) { const db drizzle(env.DB); return Response.json(await db.select().from(users)); } }阅读路线与关联参考本技能包内 D1 的完整参考共 5 份文档建议按如下顺序阅读d1/README.md —— 本指南总览 快速开始 限额d1/configuration.md —— wrangler.jsonc 配置、迁移、类型、ORM、本地开发d1/api.md —— 查询方法、batch、Sessions、读副本、错误处理、REST APId1/patterns.md —— 分页、批量、缓存、多租户、会话、分析d1/gotchas.md —— SQL 注入、套餐限额、性能、常见错误常用任务索引首次搭建configuration.md → 跑迁移migrations apply --remote新增查询api.md → 预备语句与四种执行方法分页/缓存patterns.md → COUNTOFFSET 分页、KV 缓存生产优化Read Replication Sessions API排障gotchas.md横向关联D1 是 Worker 生态存储栈的一员与 workers运行时与 fetch handler 模式、hyperdrive外部数据库连接池、kv键值缓存共同构成完整的数据方案认证前置步骤见 wrangler/auth.md。小结D1 的正确打开方式综合五份参考文档可以把 D1 的使用要点浓缩为四条工程纪律安全一律使用预备语句 bind()字符串拼接 SQL 是唯一不可饶恕的错误架构不要试图把一切塞进一个巨型数据库——按用户/租户/实体拆库让水平扩展替你解决容量问题性能索引先行EXPLAIN QUERY PLAN验证、batch()合并往返、JOIN 消灭 N1、KV 做缓存层重型操作交给 Sessions可靠性迁移必须--remote上线、读后写走主库、session.close()用 finally 兜底、Time Travel 与导出备份要纳入日常运维。遵循这些纪律D1 就能以零运维 按量计费的方式成为 Workers 应用里可靠、可扩展的关系型数据层。赞分享【免费下载链接】autoskillsOne command. Your entire AI skill stack. Installed.项目地址https://gitcode.com/gh_mirrors/au/autoskills点击查看免费下载相关推荐Cloudflare Workers 测试Miniflare 中模拟 D1 数据库的完整指南Cloudflare Workers 测试Miniflare 中模拟 D1 数据库的完整指南 Miniflare 是 Cloudflare 官方提供的 Wor文档Mastra 集成 Cloudflare D1 存储指南从 Workers Binding 到 REST API 的完整实战Mastra 集成 Cloudflare D1 存储指南从 Workers Binding 到 REST API 的完整实战 Mastra 是基于 TypeS人工智能Agent 框架AI AgentRAG后端在 Cloudflare Workers 上为 VoltAgent 接入 D1 持久化voltagent/cloudflare-d1 存储适配器实战指南在 Cloudflare Workers 上为 VoltAgent 接入 D1 持久化voltagent/cloudflare d1 存储适配器实战指南 本人工智能AI AgentAgent 框架后端多智能体RAG工具调用Agent 记忆Agent 工作流AI 评测MCP 服务MCP Clients语音创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进