Deno KV:内置键值存储
Deno KV 是 Deno 内置的持久化键值数据库,无需安装任何依赖。本地运行时基于 SQLite,部署到 Deno Deploy 时自动切换为全球分布式的 FoundationDB,数据自动持久化和同步。
-
键(Key)
由字符串、数字、Uint8Array 或 bigint 组成的元组数组,如
["users", 123]、["counters", "page_views"]。层级结构便于批量查询。 - 值(Value) 任意可结构化克隆的 JavaScript 值:对象、数组、string、number、Map、Set、ArrayBuffer、Date 等。最大 64KB。
- 原子操作 多个 KV 操作在一个事务中原子执行,要么全部成功,要么全部回滚。支持 check-and-set(CAS)语义。
基本 CRUD 操作
// kv-basic.ts
const kv = await Deno.openKv(); // 本地: ./kv.db,Deploy: FoundationDB
// set — 写入数据
await kv.set(["users", "alice"], {
name: "Alice",
email: "alice@example.com",
createdAt: new Date(),
});
// get — 读取数据
const result = await kv.get<{ name: string; email: string }>(["users", "alice"]);
console.log(result.value?.name); // "Alice"
console.log(result.versionstamp); // 版本号,用于乐观锁
// delete — 删除
await kv.delete(["users", "alice"]);
// list — 范围查询(按前缀)
const iter = kv.list({ prefix: ["users"] });
for await (const entry of iter) {
console.log(entry.key, entry.value);
}
// getMany — 批量读取
const [user1, user2] = await kv.getMany([
["users", "alice"],
["users", "bob"],
]);
kv.close();
原子操作与事务
// 原子转账:从 A 账户扣款,向 B 账户加款
async function transfer(from: string, to: string, amount: number) {
const [fromEntry, toEntry] = await kv.getMany<number>([
["balance", from],
["balance", to],
]);
const fromBalance = fromEntry.value ?? 0;
if (fromBalance < amount) throw new Error("余额不足");
const res = await kv.atomic()
// 乐观锁:确保读取后没有被修改
.check(fromEntry)
.check(toEntry)
// 原子写入
.set(["balance", from], fromBalance - amount)
.set(["balance", to], (toEntry.value ?? 0) + amount)
.commit();
if (!res.ok) throw new Error("事务冲突,请重试");
}
Deno Deploy 自动 KV 持久化
零配置持久化:代码原封不动部署到 Deno Deploy 后,Deno.openKv() 会自动连接到 FoundationDB 全球分布式存储。本地开发用 SQLite,云端自动切换为分布式 KV——同一套代码,无需任何修改。
连接 PostgreSQL
// 使用 npm:postgres 驱动(Deno 2 完整 npm 兼容)
import postgres from "npm:postgres@3";
const sql = postgres(Deno.env.get("DATABASE_URL")!, {
max: 10, // 连接池大小
idle_timeout: 20,
});
// 查询
const users = await sql`
SELECT id, name, email
FROM users
WHERE active = ${true}
ORDER BY created_at DESC
LIMIT ${10}
`;
// 插入
const [newUser] = await sql`
INSERT INTO users (name, email)
VALUES (${name}, ${email})
RETURNING *
`;
// 事务
await sql.begin(async (tx) => {
await tx`UPDATE accounts SET balance = balance - ${amount} WHERE id = ${fromId}`;
await tx`UPDATE accounts SET balance = balance + ${amount} WHERE id = ${toId}`;
});
// deno run --allow-net --allow-env pg-demo.ts
SQLite:@db/sqlite
// 使用官方 @db/sqlite
import { Database } from "jsr:@db/sqlite@^0.12";
const db = new Database("./app.db");
// 建表
db.exec(`
CREATE TABLE IF NOT EXISTS users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created_at TEXT DEFAULT CURRENT_TIMESTAMP
)
`);
// 插入(预编译语句)
const insert = db.prepare("INSERT INTO users (name, email) VALUES (?, ?)");
insert.run("Alice", "alice@example.com");
// 查询
const users = db.prepare("SELECT * FROM users").all();
console.log(users);
db.close();
// deno run --allow-read --allow-write sqlite-demo.ts
Redis:npm:ioredis
import Redis from "npm:ioredis@5";
const redis = new Redis(Deno.env.get("REDIS_URL")!);
// 基本操作
await redis.set("key", "value", "EX", 3600); // 1小时过期
const val = await redis.get("key");
// 缓存模式
async function getUser(id: number) {
const cached = await redis.get(`user:${id}`);
if (cached) return JSON.parse(cached);
// 从数据库查询...
const user = { id, name: "Alice" };
await redis.setex(`user:${id}`, 300, JSON.stringify(user));
return user;
}
实战:Deno KV 计数器与会话系统
// session-counter.ts — 用 Deno KV 实现原子计数器 + 会话管理
const kv = await Deno.openKv();
// 原子计数器
async function incrementCounter(name: string): Promise<number> {
const key = ["counters", name];
let retries = 5;
while (retries-- > 0) {
const entry = await kv.get<number>(key);
const current = entry.value ?? 0;
const result = await kv.atomic()
.check(entry)
.set(key, current + 1)
.commit();
if (result.ok) return current + 1;
}
throw new Error("计数器操作失败");
}
// 会话管理
interface Session { userId: string; createdAt: number; data: Record<string, unknown>; }
async function createSession(userId: string): Promise<string> {
const sessionId = crypto.randomUUID();
await kv.set(["sessions", sessionId], {
userId, createdAt: Date.now(), data: {}
}, { expireIn: 86400_000 }); // 24小时过期
return sessionId;
}
async function getSession(sessionId: string) {
return (await kv.get<Session>(["sessions", sessionId])).value;
}
// HTTP 服务器集成
Deno.serve(async (req) => {
const url = new URL(req.url);
if (url.pathname === "/count") {
const count = await incrementCounter("page_views");
return Response.json({ count });
}
return new Response("OK");
});
本章小结:Deno KV 是 Deno 最具差异化的特性——零配置内置存储,本地 SQLite,云端自动切换 FoundationDB,原子操作保证数据一致性。PostgreSQL 通过 npm:postgres 无缝接入,SQLite 通过 @db/sqlite 使用,Redis 通过 npm:ioredis 访问。Deno 2 的 npm 兼容性使数据库生态完整可用。