下面我将为您概述 Durable Objects 存储 API 的用法,并结合文档内容进行解释。
核心概述
Durable Objects 存储 API 为每个对象实例提供了一个私有的、事务性的、强一致性的存储空间。 这意味着数据是安全的、最新的,并且操作是原子的(要么全部成功,要么全部失败)。
根据文档,存储后端主要有两种:
-
KV (键值对) 模式:这是传统的存储方式。
-
SQLite 模式:这是 Cloudflare 推荐的新模式,它在对象内部嵌入了一个完整的 SQLite 数据库,不仅支持键值对操作,还支持功能强大的 SQL 查询。
无论使用哪种模式,你都通过 this.state.storage (在旧版 Worker 中) 或 this.ctx.storage (在新版模块 Worker 中) 来访问存储 API。
1. 键值对 (KV) API 用法
这些是最常用、最基础的操作,在 SQLite 和 KV 两种模式下都可用。
put() - 存储或更新数据
此方法用于存储一个或多个键值对。 如果键已存在,其值将被覆盖。
-
单个存储:
// 存储一个字符串值 await this.ctx.storage.put("username", "alice"); // 存储一个数字和对象 await this.ctx.storage.put("loginCount", 15); await this.ctx.storage.put("profile", { email: "test@example.com", plan: "pro" }); -
批量存储: 可以一次性存储多个键值对,最多支持 128 个。
await this.ctx.storage.put({ username: "bob", loginCount: 1, lastLogin: Date.now() });
get() - 读取数据
此方法用于读取一个或多个键的值。 如果键不存在,则返回 undefined。
-
单个读取:
let username = await this.ctx.storage.get("username"); // 返回 "alice" 或 undefined let profile = await this.ctx.storage.get("profile"); // 返回存储的对象 -
批量读取: 一次性读取多个键,返回一个
Map对象。 任何不存在的键都不会出现在返回的 Map 中。 最多支持 128 个键。let data = await this.ctx.storage.get(["username", "loginCount", "nonexistentKey"]); // data 是一个 Map: // data.get("username") -> "bob" // data.get("loginCount") -> 1 // data.has("nonexistentKey") -> false
delete() - 删除数据
删除一个或多个键值对。
-
单个删除: 如果键存在并被成功删除,返回
true,否则返回false。let wasDeleted = await this.ctx.storage.delete("old-key"); -
批量删除: 返回成功删除的键值对的数量。 最多支持 128 个键。
let deletedCount = await this.ctx.storage.delete(["temp-key-1", "temp-key-2"]);
list() - 列出键值对
返回对象中存储的所有或部分键值对,结果是一个 Map。
警告: 如果不加任何选项直接调用 list(),它会尝试将对象中的所有数据加载到内存中,这可能导致超出内存限制。
-
常用选项:
-
prefix: 只列出以特定字符串开头的键。 -
limit: 限制返回的键值对数量。 -
start/startAfter: 从哪个键开始(或之后)列出。 -
reverse:true表示降序排列。
-
-
示例:
// 获取所有以 "user:" 开头的键值对 let userEntries = await this.ctx.storage.list({ prefix: "user:" }); // 获取前 10 个键值对 let firstTen = await this.ctx.storage.list({ limit: 10 });
deleteAll() - 删除所有数据
清空该 Durable Object 实例中的所有数据。 在 SQLite 模式下,此操作是原子的;在旧的 KV 模式下,如果中途失败可能只删除了一部分。
await this.ctx.storage.deleteAll();
2. SQL API 用法 (仅限 SQLite 模式)
这是 SQLite 模式独有的强大功能,允许你执行原生 SQL 查询。 通过 this.ctx.storage.sql 访问。
sql.exec() - 执行 SQL 查询
这是核心方法,用于执行任何 SQL 语句。
-
参数:
-
query: 包含 SQL 语句的字符串。 可以用?作为参数占位符。 -
...bindings: 传递给?占位符的实际值。
-
-
返回值:
返回一个可迭代的
cursor对象,你可以用它来遍历查询结果。 -
示例:
// 在构造函数中初始化表结构 constructor(ctx, env) { super(ctx, env); this.sql = this.ctx.storage.sql; // 如果表不存在,就创建它 this.sql.exec(` CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY, name TEXT, email TEXT )`); } // --- 在其他方法中使用 --- // 插入数据 (使用参数绑定) this.sql.exec("INSERT INTO users (name, email) VALUES (?, ?)", "Charlie", "charlie@example.com"); // 查询数据并遍历结果 let cursor = this.sql.exec("SELECT * FROM users WHERE name LIKE ?", "C%"); for (const row of cursor) { console.log(row.id, row.name); // 输出: 1 Charlie } // 将查询结果直接转为数组 let allUsers = this.sql.exec("SELECT * FROM users").toArray(); // allUsers 是一个对象数组: [{ id: 1, name: 'Charlie', ... }]
3. 事务 (Transactions) 的重要概念
Durable Objects 的一个关键优势是其内置的事务性保证。
-
自动事务: 文档指出,显式的
transaction()API 在很多情况下已不再是必需的。 系统会自动将操作打包处理:-
自动写入合并: 如果你连续调用多个
put()或delete()而中间没有await,它们会被自动合并成一个原子操作提交。 -
读写原子性: 当你执行一个
await读取操作(如get())时,系统会自动阻止其他事件并发执行,直到读取完成。 因此,“先读后写”的模式(await get(), 然后put())天然就是事务性的和安全的。
-
这个特性极大地简化了代码,让你无需手动管理锁和事务就能编写出正确、无竞态条件的代码。