最近,Cloudflare 推出了 D1 数据库的正式版。这是一个基于 SQLite 的边缘数据库,可以直接在 Cloudflare Workers 中使用,不需要自己搭建数据库服务器。
本文介绍 D1 的基本用法。
D1 是 Cloudflare 提供的 Serverless 数据库,底层是 SQLite。
它的核心特点:
简单说,D1 就是把 SQLite 搬到了云端,让你在 Worker 里直接读写数据库。
你需要:
npm install -g wrangler登录 Cloudflare:
wrangler login浏览器会弹出授权页面,点确认即可。
一条命令就够了:
wrangler d1 create my-db输出类似这样:
✅ Successfully created DB 'my-db'
[[d1_databases]]
binding = "MY_DB"
database_name = "my-db"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
记住这里的 database_id,后面要用。
新建一个项目目录,创建 wrangler.toml 文件:
name = "my-d1-app"
main = "src/index.js"
compatibility_date = "2024-12-01"
[[d1_databases]]
binding = "MY_DB"
database_name = "my-db"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"注意,binding 的值是 "MY_DB",后面代码里会通过 env.MY_DB 访问数据库。这个值可以自定义,但必须和代码里一致。
创建一个 SQL 文件 schema.sql:
CREATE TABLE IF NOT EXISTS todos (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
completed INTEGER NOT NULL DEFAULT 0,
created_at INTEGER NOT NULL DEFAULT (unixepoch())
);执行建表:
wrangler d1 execute my-db --remote --file=schema.sql--remote 表示操作远程数据库。如果省略,操作的是本地数据库。
同样用 SQL 文件,避免中文乱码问题。
创建 seed.sql(文件编码必须是 UTF-8):
INSERT INTO todos (title) VALUES ('学习 Cloudflare Workers');
INSERT INTO todos (title) VALUES ('写一篇 D1 教程');
INSERT INTO todos (title) VALUES ('部署上线');执行:
wrangler d1 execute my-db --remote --file=seed.sql也可以用 --command 参数执行单条 SQL,但不建议传中文,Windows 终端编码容易出问题:
# 英文数据可以用 --command
wrangler d1 execute my-db --remote --command="INSERT INTO todos (title) VALUES ('hello world')"
# 中文数据请用 --file创建 src/index.js:
export default {
async fetch(request, env) {
const url = new URL(request.url);
// 获取所有待办事项
if (url.pathname === "/api/todos") {
const { results } = await env.MY_DB.prepare(
"SELECT * FROM todos ORDER BY created_at DESC"
).all();
return Response.json(results);
}
// 新增待办事项
if (url.pathname === "/api/todos" && request.method === "POST") {
const { title } = await request.json();
const { meta } = await env.MY_DB.prepare(
"INSERT INTO todos (title) VALUES (?)"
).bind(title).run();
return Response.json({ id: meta.last_row_id, title }, { status: 201 });
}
// 切换完成状态
if (url.pathname.startsWith("/api/todos/") && request.method === "PATCH") {
const id = url.pathname.split("/").pop();
await env.MY_DB.prepare(
"UPDATE todos SET completed = NOT completed WHERE id = ?"
).bind(id).run();
return Response.json({ success: true });
}
// 删除
if (url.pathname.startsWith("/api/todos/") && request.method === "DELETE") {
const id = url.pathname.split("/").pop();
await env.MY_DB.prepare(
"DELETE FROM todos WHERE id = ?"
).bind(id).run();
return Response.json({ success: true });
}
return new Response("Todo API: GET/POST /api/todos, PATCH/DELETE /api/todos/:id");
},
};代码不长,但有几个要点:
env.MY_DB 就是 wrangler.toml 中 binding 对应的值。.prepare(sql) 预编译 SQL,.bind(params) 绑定参数,防止 SQL 注入。.all() 返回所有结果,写入用 .run() 返回 meta 信息。D1 的原生 API 很简单,只有几个方法:
| 方法 | 用途 | 返回值 |
|---|---|---|
.prepare(sql) |
预编译 SQL | Statement 对象 |
.bind(...params) |
绑定参数 | Statement 对象 |
.all() |
查询所有结果 | { results: [] } |
.first() |
查询第一条 | 对象 或 null |
.run() |
执行写入操作 | { meta: { last_row_id, changes } } |
.batch([stmt1, stmt2]) |
批量执行(事务) | 结果数组 |
最常用的就是 all()、first() 和 run()。
// 查询所有
const { results } = await env.MY_DB.prepare("SELECT * FROM todos").all();
// 查询单条
const todo = await env.MY_DB.prepare("SELECT * FROM todos WHERE id = ?").bind(1).first();
// 带条件查询
const { results } = await env.MY_DB.prepare(
"SELECT * FROM todos WHERE completed = ?"
).bind(0).all();// 插入
const { meta } = await env.MY_DB.prepare(
"INSERT INTO todos (title) VALUES (?)"
).bind("新任务").run();
const newId = meta.last_row_id; // 自增 ID
// 更新
const { meta } = await env.MY_DB.prepare(
"UPDATE todos SET completed = ? WHERE id = ?"
).bind(1, 5).run();
const affectedRows = meta.changes; // 影响行数
// 删除
await env.MY_DB.prepare("DELETE FROM todos WHERE id = ?").bind(5).run();// batch 是隐式事务,要么全成功,要么全回滚
const results = await env.MY_DB.batch([
env.MY_DB.prepare("INSERT INTO todos (title) VALUES (?)").bind("任务A"),
env.MY_DB.prepare("INSERT INTO todos (title) VALUES (?)").bind("任务B"),
env.MY_DB.prepare("INSERT INTO todos (title) VALUES (?)").bind("任务C"),
]);D1 支持本地开发,数据存在 .wrangler/state/ 目录下。
wrangler dev访问 http://localhost:8787,所有操作都在本地数据库执行,不会影响线上数据。
本地数据库的建表和导入数据,去掉 --remote 参数即可:
wrangler d1 execute my-db --file=schema.sql
wrangler d1 execute my-db --file=seed.sqlwrangler deploy部署后,你的 Worker 就有了全局 URL,比如:
https://my-d1-app.你的子域.workers.dev/api/todos
原因:Windows 终端默认编码是 GBK,通过 --command 传中文会乱码。
解决:用 SQL 文件(UTF-8 编码)代替 --command,或者通过 Worker API 插入。
原因:代码中 env.xxx 的绑定名和 wrangler.toml 中的 binding 不一致。
解决:确保两者完全一致。比如 binding = "MY_DB",代码里就写 env.MY_DB。
原因:数据库没有数据,或者建表/导入操作没有加 --remote。
解决:
# 检查远程数据库是否有数据
wrangler d1 execute my-db --remote --command="SELECT * FROM todos"| 指标 | 免费版 | 付费版 |
|---|---|---|
| 存储空间 | 5GB | 25GB |
| 每日读取 | 500万 | 250亿 |
| 每日写入 | 10万 | 5000万 |
| 单次查询行数 | 1000 | 1000 |
| 单库大小 | 10GB | 10GB |
D1 适合中小规模的应用。如果你的数据量很大,或者需要完整的 PostgreSQL 功能,可以考虑 Turso 或 Supabase。
(完)
留言(0 条)
使用 GitHub 登录后即可在下方直接留言