名称:cloudflare-d1 描述:Cloudflare D1 边缘无服务器 SQLite。用于数据库、迁移、绑定,或遇到 D1_ERROR、语句过长、请求队列过多错误。
关键词:d1, d1 数据库, cloudflare d1, wrangler d1, d1 迁移, d1 绑定, sqlite workers, 边缘数据库, d1 查询, sql cloudflare, 预处理语句, 批量查询, d1 api, wrangler 迁移, D1_ERROR, D1_EXEC_ERROR, 语句过长, 数据库绑定, sqlite cloudflare, sql workers api, d1 索引, 查询优化, d1 模式, 读取复制, 读取副本, withSession, Sessions API, 全局复制, 数据库复制, served_by_region, 书签, 顺序一致性 许可证:MIT 元数据: 版本:“3.0.0” 最后验证:“2025-01-15” 生产测试:true 令牌节省:“~58%” 错误预防:8 包含模板:3 包含参考:4 wrangler版本:“4.50.0” workers类型版本:“4.20251125.0” drizzle_orm版本:“0.44.7”
Cloudflare D1 数据库
状态:生产就绪 ✅ | 最后验证:2025-01-15
目录
什么是 D1?
Cloudflare D1 是边缘无服务器 SQLite:
- 无服务器 SQL 数据库
- 全球分布
- 零冷启动
- 标准 SQLite 语法
- 读取复制以实现全局性能
🆕 2025 年新功能
D1 在 2025 年进行了重大更新:
性能(2025 年 1 月)
- 全局延迟减少 40-60%(P50 查询时间)
- 针对边缘执行优化的 SQLite 引擎
- 对小于 100 MB 的数据库减少冷启动影响
可靠性(2025 年 9 月)
- 自动查询重试:读取查询在瞬时故障时最多重试 2 次
- 对应用程序代码透明(在
wrangler tail中记录)
可扩展性(2025 年 4 月)
- 读取复制(公共测试版):全局部署读取副本
- 读取密集型工作负载的读取吞吐量高达 2 倍
- 用于读写分离的 Sessions API
合规性(2025 年 11 月)
- 数据本地化:为 GDPR/数据主权指定欧盟/美国管辖区
- 通过
--jurisdiction标志或 wrangler.jsonc 配置
⚠️ 重大变更(2025 年 2 月 10 日)
- 免费层硬限制强制执行:10 个数据库,每个 500 MB,每次调用 50 个查询
- 超出限制 = 429 错误(之前仅为警告)
- 行动:使用
wrangler d1 list审查使用情况,必要时升级
完整详情:加载 references/2025-features.md
快速开始(5 分钟)
1. 创建数据库
bunx wrangler d1 create my-database
保存输出中的 database_id!
2. 配置绑定
添加到 wrangler.jsonc:
{
"name": "my-worker",
"main": "src/index.ts",
"compatibility_date": "2025-10-11",
"d1_databases": [
{
"binding": "DB", // env.DB
"database_name": "my-database",
"database_id": "<UUID>",
"preview_database_id": "local-db"
}
]
}
3. 创建迁移
bunx wrangler d1 migrations create my-database create_users
编辑 migrations/0001_create_users.sql:
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL UNIQUE,
username TEXT NOT NULL,
created_at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
PRAGMA optimize;
4. 应用迁移
# 本地
bunx wrangler d1 migrations apply my-database --local
# 生产
bunx wrangler d1 migrations apply my-database --remote
5. 从 Worker 查询
import { Hono } from 'hono';
type Bindings = {
DB: D1Database;
};
const app = new Hono<{ Bindings: Bindings }>();
app.get('/users/:email', async (c) => {
const { results } = await c.env.DB.prepare(
'SELECT * FROM users WHERE email = ?'
)
.bind(c.req.param('email'))
.all();
return c.json(results);
});
export default app;
加载 references/setup-guide.md 获取完整演练。
关键规则
始终做 ✅
- 使用预处理语句 与
.bind()(永远不要字符串连接) - 为 WHERE/JOIN/ORDER BY 列创建索引
- 使用迁移 进行模式更改(永远不要手动 SQL)
- 批量查询 进行多个操作(.batch())
- 模式更改后运行 PRAGMA optimize
- 显式处理错误(try/catch)
- 对时间戳使用 INTEGER(Date.now())
- 部署迁移前本地测试
- 使用读取副本 实现全局读取性能
- SQL 查询前验证输入
永远不要做 ❌
- 永远不要将用户输入连接 到 SQL
- 永远不要将 database_id 提交 到公共仓库
- 永远不要跳过迁移 进行模式更改
- 永远不要使用 VARCHAR(使用 TEXT 代替)
- 永远不要跳过索引 用于过滤列
- 永远不要忽略 SQLite 类型亲和性规则
- **永远不要使用 SELECT *** 没有 LIMIT
- 永远不要运行迁移 而没有本地测试
- 永远不要超过 每行 1MB
- 永远不要使用 DATETIME(对时间戳使用 INTEGER)
D1 API 方法
prepare() - 执行查询
// 单结果
const { results } = await env.DB.prepare(
'SELECT * FROM users WHERE email = ?'
)
.bind(email)
.all();
// 仅第一个结果
const user = await env.DB.prepare(
'SELECT * FROM users WHERE user_id = ?'
)
.bind(userId)
.first();
// 原始结果(更快)
const { results } = await env.DB.prepare(
'SELECT username FROM users'
)
.raw(); // 返回数组而不是对象
batch() - 多个查询
const results = await env.DB.batch([
env.DB.prepare('INSERT INTO users (email, username, created_at) VALUES (?, ?, ?)')
.bind('user1@example.com', 'user1', Date.now()),
env.DB.prepare('INSERT INTO users (email, username, created_at) VALUES (?, ?, ?)')
.bind('user2@example.com', 'user2', Date.now()),
env.DB.prepare('SELECT COUNT(*) as count FROM users')
]);
console.log('用户数:', results[2].results[0].count);
所有查询在单个事务中执行(全部成功或全部失败)。
exec() - 运行 SQL 字符串
// 仅用于迁移/设置
await env.DB.exec(`
CREATE TABLE IF NOT EXISTS users (
user_id INTEGER PRIMARY KEY,
email TEXT NOT NULL
);
CREATE INDEX idx_email ON users(email);
`);
永远不要用于有用户输入的查询!
加载 references/query-patterns.md 获取完整 API 参考。
前 5 个用例
用例 1:用户 CRUD
// 创建
app.post('/users', async (c) => {
const { email, username } = await c.req.json();
const { results } = await c.env.DB.prepare(
'INSERT INTO users (email, username, created_at) VALUES (?, ?, ?) RETURNING *'
)
.bind(email, username, Date.now())
.all();
return c.json(results[0]);
});
// 读取
app.get('/users/:id', async (c) => {
const user = await c.env.DB.prepare(
'SELECT * FROM users WHERE user_id = ?'
)
.bind(c.req.param('id'))
.first();
if (!user) {
return c.json({ error: '未找到' }, 404);
}
return c.json(user);
});
// 更新
app.patch('/users/:id', async (c) => {
const { username } = await c.req.json();
await c.env.DB.prepare(
'UPDATE users SET username = ?, updated_at = ? WHERE user_id = ?'
)
.bind(username, Date.now(), c.req.param('id'))
.run();
return c.json({ success: true });
});
// 删除
app.delete('/users/:id', async (c) => {
await c.env.DB.prepare(
'DELETE FROM users WHERE user_id = ?'
)
.bind(c.req.param('id'))
.run();
return c.json({ success: true });
});
用例 2:批量操作
app.post('/users/bulk', async (c) => {
const users = await c.req.json(); // 用户数组
const statements = users.map(user =>
c.env.DB.prepare(
'INSERT INTO users (email, username, created_at) VALUES (?, ?, ?)'
).bind(user.email, user.username, Date.now())
);
const results = await c.env.DB.batch(statements);
return c.json({ inserted: results.length });
});
用例 3:读取复制(全局读取)
// 配置读取副本(任何区域)
const session = c.env.DB.withSession({
preferredRegion: 'auto' // 或 'weur', 'wnam', 'enam', 'apac'
});
// 从最近副本读取
const { results } = await session.prepare(
'SELECT * FROM users WHERE email = ?'
)
.bind(email)
.all();
// 检查哪个区域服务请求
console.log('服务区域:', results[0].served_by_region);
加载 references/read-replication.md 获取完整指南。
用例 4:使用 Batch 的事务
// 用户间转移积分(原子性)
const results = await c.env.DB.batch([
c.env.DB.prepare(
'UPDATE users SET credits = credits - ? WHERE user_id = ?'
).bind(amount, fromUserId),
c.env.DB.prepare(
'UPDATE users SET credits = credits + ? WHERE user_id = ?'
).bind(amount, toUserId),
c.env.DB.prepare(
'INSERT INTO transactions (from_user, to_user, amount, created_at) VALUES (?, ?, ?, ?)'
).bind(fromUserId, toUserId, amount, Date.now())
]);
// 全部成功或全部失败(事务)
用例 5:分页
app.get('/users', async (c) => {
const page = parseInt(c.req.query('page') || '1');
const limit = 20;
const offset = (page - 1) * limit;
const { results } = await c.env.DB.prepare(
'SELECT * FROM users ORDER BY created_at DESC LIMIT ? OFFSET ?'
)
.bind(limit, offset)
.all();
return c.json({
users: results,
page,
limit
});
});
迁移最佳实践
1. 始终使用迁移
bunx wrangler d1 migrations create my-database add_users_avatar
2. 使迁移幂等
-- ✅ 好:幂等
CREATE TABLE IF NOT EXISTS users (...);
CREATE INDEX IF NOT EXISTS idx_email ON users(email);
DROP TABLE IF EXISTS old_table;
-- ❌ 坏:重运行时失败
CREATE TABLE users (...);
CREATE INDEX idx_email ON users(email);
3. 先本地测试
bunx wrangler d1 migrations apply my-database --local
bunx wrangler d1 execute my-database --local --command "SELECT * FROM users"
4. 添加 PRAGMA optimize
-- 迁移末尾
PRAGMA optimize;
加载 templates/schema-example.sql 获取完整模式模板。
何时加载参考
加载 references/setup-guide.md 当:
- 首次 D1 设置
- 创建第一个数据库
- 配置绑定
- 应用第一个迁移
加载 references/query-patterns.md 当:
- 需要完整 API 参考
- 复杂查询模式
- 批量操作
- 错误处理
加载 references/read-replication.md 当:
- 设置全局读取
- 需要全球低延迟
- 理解 Sessions API
- 需要顺序一致性
加载 references/best-practices.md 当:
- 优化查询性能
- 模式设计决策
- 索引策略
- 生产部署清单
加载 references/limits.md 当:
- 遇到 429 错误或配额警告
- 规划免费与付费层容量
- 理解数据库/查询限制
- 迁移到付费计划
加载 references/metrics-analytics.md 当:
- 调查性能问题
- 设置监控和警报
- 使用
wrangler d1 insights命令 - 分析查询效率
加载 references/2025-features.md 当:
- 从 v2.x 升级到 v3.x
- 启用新功能(自动重试、管辖区、复制)
- 理解重大变更(2025年2月10日强制执行)
- 截止日期前迁移
交互式工具
代理(自主诊断):
agents/d1-debugger.md:9阶段诊断(配置、迁移、查询、绑定、错误、限制、性能、时间旅行)agents/d1-query-optimizer.md:性能分析(慢查询、缺失索引、优化建议)
命令(交互式向导):
commands/cloudflare-d1:setup.md:交互式首次设置向导commands/d1-create-migration.md:带验证的引导迁移创建
使用捆绑资源
参考 (references/)
- setup-guide.md - 完整设置演练
- query-patterns.md - 带示例的完整 API 参考
- read-replication.md - 全局读取副本设置
- best-practices.md - 性能和优化
模板 (templates/)
- schema-example.sql - 带索引的完整模式
- d1-worker-queries.ts - Worker 中所有查询模式
- cloudflare-d1:setup-migration.sh - 完整设置脚本
常见模式
错误处理
try {
const { results } = await env.DB.prepare(
'SELECT * FROM users WHERE email = ?'
)
.bind(email)
.all();
return c.json(results);
} catch (error) {
console.error('D1 错误:', error);
return c.json({ error: '数据库错误' }, 500);
}
原始模式(性能)
// 返回数组而不是对象(更快)
const { results } = await env.DB.prepare(
'SELECT user_id, email FROM users'
)
.raw();
// results = [[1, 'user1@example.com'], [2, 'user2@example.com']]
COUNT 查询
const count = await env.DB.prepare(
'SELECT COUNT(*) as count FROM users'
)
.first('count'); // 获取单列值
console.log('总用户数:', count);
SQLite 类型亲和性
D1 使用 SQLite 类型亲和性:
| 声明类型 | 亲和性 |
|---|---|
| INTEGER, INT | INTEGER |
| TEXT, VARCHAR, CHAR | TEXT |
| REAL, FLOAT, DOUBLE | REAL |
| BLOB | BLOB |
| (无类型) | BLOB |
最佳实践:
- 对数字使用
INTEGER - 对字符串使用
TEXT(非 VARCHAR) - 对时间戳使用
INTEGER(Date.now()) - 对二进制数据使用
BLOB
前 5 个错误预防
- SQL 注入:使用
.bind(),永远不要字符串连接 - 缺失索引:为 WHERE/JOIN 列创建索引
- 迁移失败:先本地测试
- 类型混淆:对时间戳使用 INTEGER
- 批量大小:限制批次到 <500 条语句
加载 references/best-practices.md 获取完整错误预防。
官方文档
- D1 概述:https://developers.cloudflare.com/d1/
- 入门:https://developers.cloudflare.com/d1/get-started/
- 客户端 API:https://developers.cloudflare.com/d1/build-with-d1/d1-client-api/
- 读取复制:https://developers.cloudflare.com/d1/reference/read-replication/
有问题?问题?
- 检查
references/setup-guide.md获取设置 - 审查
references/query-patterns.md获取 API 参考 - 查看
references/read-replication.md获取全局读取 - 加载
references/best-practices.md获取优化