在此页上
事务
Deno KV 存储使用乐观并发控制事务,而不是像 PostgreSQL 或 MySQL 等许多 SQL 系统那样的交互式事务。这种方法采用版本戳,它代表给定键的值的当前版本,以管理对共享资源的并发访问,而无需使用锁。当发生读取操作时,系统除了值之外,还会返回关联键的版本戳。
要执行事务,需要执行一个原子操作,该操作可以包含多个突变操作(如设置或删除)。除了这些操作之外,还提供键+版本戳对作为事务成功的条件。只有当指定的版本戳与数据库中相应键的值的当前版本匹配时,乐观并发控制事务才会提交。这种事务模型确保了数据一致性和完整性,同时允许 Deno KV 存储内的并发交互。
由于 OCC 事务是乐观的,它们可能会在提交时失败,因为原子操作中指定的版本约束被违反。当代理在读取和提交之间更新事务中使用的键时,就会发生这种情况。当这种情况发生时,执行事务的代理必须重试事务。
为了说明如何在 Deno KV 中使用 OCC 事务,此示例展示了如何为帐户账本实现一个 transferFunds(from: string, to: string, amount: number)
函数。帐户账本在键值存储中存储每个帐户的余额。键以 "account"
为前缀,后跟帐户标识符:["account", "alice"]
。为每个键存储的值是一个数字,表示帐户余额。
这是一个实现 transferFunds
函数的逐步示例
async function transferFunds(sender: string, receiver: string, amount: number) {
if (amount <= 0) throw new Error("Amount must be positive");
// Construct the KV keys for the sender and receiver accounts.
const senderKey = ["account", sender];
const receiverKey = ["account", receiver];
// Retry the transaction until it succeeds.
let res = { ok: false };
while (!res.ok) {
// Read the current balance of both accounts.
const [senderRes, receiverRes] = await kv.getMany([senderKey, receiverKey]);
if (senderRes.value === null) {
throw new Error(`Account ${sender} not found`);
}
if (receiverRes.value === null) {
throw new Error(`Account ${receiver} not found`);
}
const senderBalance = senderRes.value;
const receiverBalance = receiverRes.value;
// Ensure the sender has a sufficient balance to complete the transfer.
if (senderBalance < amount) {
throw new Error(
`Insufficient funds to transfer ${amount} from ${sender}`,
);
}
// Perform the transfer.
const newSenderBalance = senderBalance - amount;
const newReceiverBalance = receiverBalance + amount;
// Attempt to commit the transaction. `res` returns an object with
// `ok: false` if the transaction fails to commit due to a check failure
// (i.e. the versionstamp for a key has changed)
res = await kv.atomic()
.check(senderRes) // Ensure the sender's balance hasn't changed.
.check(receiverRes) // Ensure the receiver's balance hasn't changed.
.set(senderKey, newSenderBalance) // Update the sender's balance.
.set(receiverKey, newReceiverBalance) // Update the receiver's balance.
.commit();
}
}
在此示例中,transferFunds
函数读取两个帐户的余额和版本戳,计算转账后的新余额,并检查帐户 A 中是否有足够的资金。然后,它执行原子操作,使用版本戳约束设置新余额。如果事务成功,则循环退出。如果版本约束被违反,事务将失败,并且循环将重试事务直到成功。
限制 跳转到标题
除了最大键大小为 2 KiB 和最大值大小为 64 KiB 之外,Deno KV 事务 API 还有一些限制
- 每次
kv.getMany()
的最大键数: 10 - 每次
kv.list()
的最大批次大小: 1000 - 原子操作中的最大检查次数: 100
- 原子操作中的最大突变次数: 1000
- 原子操作的最大总大小:800 KiB。这包括检查和突变中的所有键和值,并且编码开销也计入此限制。
- 键的最大总大小:90 KiB。这包括检查和突变中的所有键,并且编码开销也计入此限制。
- 每次
kv.watch()
监视的最大键数:10