D1
D1 是通过绑定供 Worker 访问的 SQL 数据库。在 celld 中,每个数据库都有独立的 SQLite 文件,并位于一个单元内。API 的详细说明请参阅 Cloudflare D1 文档。
示例#
D1 示例 使用 D1 数据库实现留言簿。
wrangler.jsonc
{
"name": "d1",
"main": "index.js",
"compatibility_date": "2026-01-01",
"d1_databases": [
{
"binding": "DB",
"database_name": "guestbook",
"database_id": "00000000-0000-0000-0000-000000000000"
}
]
}index.js
const SCHEMA = `
CREATE TABLE IF NOT EXISTS entries (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
message TEXT NOT NULL,
at INTEGER NOT NULL
);
CREATE INDEX IF NOT EXISTS entries_at ON entries (at DESC);
`;
export default {
async fetch(request, env) {
await env.DB.exec(SCHEMA);
const url = new URL(request.url);
if (request.method === "POST") {
const { name, message } = await request.json();
const written = await env.DB
.prepare("INSERT INTO entries (name, message, at) VALUES (?, ?, ?)")
.bind(name, message, Date.now())
.run();
return Response.json({
id: written.meta.last_row_id,
changes: written.meta.changes,
}, { status: 201 });
}
const count = await env.DB
.prepare("SELECT count(*) AS n FROM entries")
.first("n");
if (url.pathname === "/count") return Response.json({ count });
const { results } = await env.DB
.prepare(
"SELECT id, name, message, at FROM entries ORDER BY at DESC LIMIT ?",
)
.bind(20)
.all();
return Response.json({ count, entries: results });
},
};API#
prepare(sql)返回一条预处理语句;bind(...values)返回携带这些值的新语句。all()返回success、由行对象组成的results数组以及meta。run()是同一方法的另一个名称。first()返回第一行或null;first(column)返回该行中的某一列。raw()以数组形式返回各行;raw({ columnNames: true })会将列名放在第一个数组中。batch(statements)在一个事务中执行多条预处理语句,并为每条语句返回一个结果。exec(sql)执行一条或多条不带参数的语句,例如建表语句。
配置与限制#
d1_databases 配置项包含 binding、database_name 和 database_id。celld 使用 database_id 作为数据库标识;未提供 ID 时则使用 database_name。两个 Worker 使用同一个标识时,会访问同一个数据库。celld 在首次使用时创建数据库,因此名称拼写错误会打开一个空数据库,而不会报错。
bind() 会检查每个值。数字和字符串可直接使用,布尔值转换为 1 或 0,ArrayBuffer、类型化数组或由字节数值组成的数组转换为 BLOB。其他值会触发 D1_TYPE_ERROR。celld 支持绑定位置参数 ? 和 ?NNN。
celld 执行以下 D1 限制:语句最多 100,000 字节,绑定参数最多 100 个,每张表最多 100 列,字符串、BLOB 或单行最多 2,200,000 字节。
预处理语句中如果包含第二条语句,会报错 A prepared SQL statement must contain only one statement.(SQL 预处理语句只能包含一条语句)。需要执行多条语句时,请使用 exec() 或 batch()。两者都会在一个事务内执行,因此任何失败都会回滚整次调用。
每条语句都会发送到拥有该数据库单元的节点,因此每次调用需要一次单元分发。将相关语句合并到一个 batch() 中,整个批次只需要一次分发。100,000 行的结果上限不适用于 first()。已确认的写入在拥有者节点丢失后仍然保留。
withSession() 始终选择主库。getBookmark() 返回 celld:primary,meta.served_by_primary 始终为 true,meta.served_by_region 始终为 local。
应用 SQL 不能使用 BEGIN、COMMIT、ROLLBACK、SAVEPOINT、ATTACH、DETACH、VACUUM、load_extension(),也不能使用临时表、索引、触发器或视图。celld 允许 fts5 和 fts5vocab 虚拟表,并接受 D1 SQL 语句参考中列出的 PRAGMA。
celld d1 execute 和 celld d1 migrations apply 通过正在运行的节点从命令行执行 SQL。
与 Cloudflare 的差异#
- 绑定返回的结果最多包含 100,000 行或 32 MiB 数据。
- 如果 SQLite 的
TEXT值不是有效的 UTF-8,会像 workerd 一样使用替换字符 U+FFFD 解码,存储的原始字节保持不变。任意字节数据应存储为BLOB。 - 每个数据库只有一个写入者。增加数据库数量可以提高写入容量。
- celld 没有只读副本。
withSession()始终选择主库,getBookmark()返回celld:primary。 - 只读语句的
last_row_id和changes均为0。Cloudflare 则返回同一连接上上一次写入留下的值。 prepare()只能接受一条语句。exec()按语句计数;Cloudflare 按输入行计数。dump()会抛出异常。数据库就是运维者自有存储桶中的 SQLite 文件。- 应用 SQL 不能使用
BEGIN、SAVEPOINT、ATTACH、VACUUM、load_extension()或临时表。 - celld 没有 D1 REST API,也没有 Time Travel。执行 SQL 和迁移请使用
celld d1。
Cloudflare 兼容性页面列出了运行时 API 和不支持的服务。