秒杀传统数据库!Cloudflare D1 + Drizzle组合拳,高并发高可用,让我们的成本爆降10倍 - D1
想象一下:我们的应用用户量稳步增长,传统数据库的成本和维护压力也随之上升。而在这个时代,有没有更高效、更经济的数据库解决方案?Cloudflare D1 结合 Drizzle ORM 的组合,正在为众多出海应用开发提供一条全新的技术路径。
传统数据库方案在高并发场景下往往需要复杂的扩容、分片和负载均衡,成本随着流量呈指数级增长。而当我们了解了 Cloudflare D1 这款基于 SQLite 构建的边缘数据库,再配合 Drizzle 这个轻量级 ORM 的强大能力,我们会惊讶于这个组合如何能在保持高性能的同时,将我们的基础设施成本直接腰斩!
无需复杂的数据库集群,无需昂贵的专用服务器,无需担心地理位置带来的延迟问题 --- 这个方案将彻底改变我们对数据库架构的认知。
Cloudflare D1 实战:从零开始搭建高性能数据库
Cloudflare D1 是 Cloudflare 推出的一款分布式 SQL 数据库,它基于 SQLite 构建,完全集成在 Cloudflare Workers 生态系统中。D1 将 SQLite 数据库部署到 Cloudflare 的全球边缘网络,让我们的数据库与应用代码一样,运行在离用户最近的位置,大幅降低延迟。
D1成本计算与对比
在深入技术细节前,让我们先来看看 D1 在成本方面的巨大优势。Cloudflare D1 采用了极具竞争力的定价模型:
资源类型 | Workers Free (免费版) | Workers Paid (付费版) |
---|---|---|
读取行数 | 每天500万行限制 | 每月前250亿行免费,超出部分 $0.001/百万行 |
写入行数 | 每天10万行限制 | 每月前5000万行免费,超出部分 $1.00/百万行 |
存储空间 | 总计5GB限制 | 前5GB免费,超出部分 $0.75/GB-月 |
让我们来分析一下免费版的套餐:
-
读取成本:每天500万行的读取量,一个月约1.5亿行,完全在免费额度内。即使你的应用流量翻倍,达到每天1000万行读取,每月3亿行,超出免费额度的5000万行只需要额外支付 $0.05/月。
-
写入成本:每天10万行的写入,一个月约300万行,远低于免费额度的5000万行。即使写入量增长10倍,仍然在免费额度内。
-
存储成本:5GB的存储空间完全免费。对于大多数中小型应用来说,这已经足够存储数百万条记录
付费版的价格是5$,免费版的规模足够处理5000-20000日活的应用,付费20000-100w日活。
快速上手 D1
1. 安装 Wrangler CLI
首先,我们需要安装 Cloudflare 的 Wrangler CLI 工具:
bash
npm install -g wrangler
2. 创建 D1 数据库
登录我们的 Cloudflare 账户后,创建一个新的 D1 数据库:
bash
wrangler login
wrangler d1 create my-database
执行后,我们会看到类似这样的输出:
python
✅ Successfully created DB 'my-database' in region APAC
Created D1 database 'my-database' with id: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
请记下这个数据库 ID,我们后续会用到。
3. 配置 wrangler.toml
在我们的项目根目录创建或编辑 wrangler.toml
文件,添加 D1 数据库配置:
toml
[[d1_databases]]
binding = "DB" # 在 Workers 中使用的变量名
database_name = "my-database"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" # 替换为我们的数据库 ID
4. 创建数据表
创建一个 SQL 文件,例如 schema.sql
:
sql
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
然后执行:
bash
wrangler d1 execute my-database --file=./schema.sql
5. 在 Workers 中使用 D1
现在,我们可以在 Cloudflare Workers 中使用 D1 数据库了:
typescript
export interface Env {
DB: D1Database
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// 查询用户列表
const { results } = await env.DB.prepare('SELECT * FROM users ORDER BY created_at DESC LIMIT 10').all()
return new Response(JSON.stringify(results), {
headers: { 'Content-Type': 'application/json' }
})
}
}
D1 的实用命令与简单实践
在实际开发中,我们需要更多的工具来管理数据库。D1 提供了一系列强大的命令行工具,让数据库管理变得轻松高效。
数据库迁移:管理我们的架构变更
数据库结构会随着需求不断变化。D1 提供了完善的迁移系统,让我们可以版本化管理数据库结构:
bash
# 创建一个新的迁移文件
wrangler d1 migrations create my-database add_user_role
这会在项目中创建一个类似 migrations/0001_add_user_role.sql
的文件。编辑这个文件,添加我们的 SQL 变更:
sql
-- Migration: add_user_role
-- Created at: 2023-10-15 14:30:00
-- 向用户表添加角色字段
ALTER TABLE users ADD COLUMN role TEXT DEFAULT 'user' NOT NULL;
-- 创建一个新的角色权限表
CREATE TABLE role_permissions (
role TEXT NOT NULL,
permission TEXT NOT NULL,
PRIMARY KEY (role, permission)
);
然后应用这些迁移:
bash
# 应用到本地开发环境
wrangler d1 migrations apply my-database --local
# 应用到生产环境
wrangler d1 migrations apply my-database --remote
这种方式让我们可以:
- 追踪数据库的所有变更历史
- 在团队中同步数据库结构
- 在不同环境(开发、测试、生产)之间保持一致性
数据导入导出:备份与恢复
需要备份数据或将数据迁移到其他环境?D1 提供了简单的导出导入功能:
bash
# 导出整个数据库(结构+数据)
wrangler d1 export my-database --output=backup.sql
# 只导出特定表
wrangler d1 export my-database --table=users --output=users_backup.sql
# 只导出结构,不导出数据
wrangler d1 export my-database --output=schema.sql --no-data
导入数据同样简单:
bash
wrangler d1 execute my-database --file=backup.sql
实战案例:构建一个博客系统
让我们通过一个实际案例来展示 D1 的强大功能。假设我们要构建一个简单的博客系统,需要存储文章和评论。
首先,创建数据库结构:
sql
-- migrations/0001_create_blog_tables.sql
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
content TEXT NOT NULL,
author_id INTEGER NOT NULL,
published_at DATETIME DEFAULT CURRENT_TIMESTAMP,
status TEXT DEFAULT 'draft' NOT NULL
);
CREATE TABLE comments (
id INTEGER PRIMARY KEY AUTOINCREMENT,
post_id INTEGER NOT NULL,
author_name TEXT NOT NULL,
content TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (post_id) REFERENCES posts(id) ON DELETE CASCADE
);
CREATE INDEX idx_posts_status ON posts(status);
CREATE INDEX idx_comments_post_id ON comments(post_id);
npx wrangler d1 execute prod-d1-tutorial --local --file=./migrations/0001_create_blog_tables.sql
然后,在 Workers 中实现 API 接口:
typescript
export interface Env {
DB: D1Database
}
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url)
const path = url.pathname
// 获取博客文章列表
if (path === '/api/posts' && request.method === 'GET') {
const { results } = await env.DB.prepare(
"SELECT id, title, published_at FROM posts WHERE status = 'published' ORDER BY published_at DESC LIMIT 10"
).all()
return new Response(JSON.stringify(results), {
headers: { 'Content-Type': 'application/json' }
})
}
// 获取单篇文章及其评论
if (path.match(/^\/api\/posts\/\d+$/) && request.method === 'GET') {
const postId = path.split('/').pop()
// 获取文章详情
const post = await env.DB.prepare('SELECT * FROM posts WHERE id = ?').bind(postId).first()
if (!post) {
return new Response(JSON.stringify({ error: 'Post not found' }), {
status: 404,
headers: { 'Content-Type': 'application/json' }
})
}
// 获取文章评论
const { results: comments } = await env.DB.prepare(
'SELECT * FROM comments WHERE post_id = ? ORDER BY created_at DESC'
)
.bind(postId)
.all()
return new Response(JSON.stringify({ post, comments }), {
headers: { 'Content-Type': 'application/json' }
})
}
// 添加评论
if (path.match(/^\/api\/posts\/\d+\/comments$/) && request.method === 'POST') {
const postId = path.split('/')[3]
const { author_name, content } = await request.json()
// 插入评论
const result = await env.DB.prepare(
'INSERT INTO comments (post_id, author_name, content) VALUES (?, ?, ?) RETURNING id'
)
.bind(postId, author_name, content)
.run()
return new Response(JSON.stringify({ id: result.results[0].id }), {
status: 201,
headers: { 'Content-Type': 'application/json' }
})
}
return new Response('Not Found', { status: 404 })
}
}
这个简单的博客 API 已经能够:
- 获取已发布的文章列表
- 获取单篇文章及其评论
- 为文章添加新评论
本地开发与调试
在开发过程中,我们可以使用本地数据库进行测试:
bash
# 启动本地开发服务器,使用本地 D1 数据库
wrangler dev --local
这会在本地创建一个 SQLite 数据库文件,我们可以在开发过程中使用它,而不需要每次都操作远程数据库。当我们的代码准备好后,再将变更应用到远程数据库。
bash
# 应用迁移到远程数据库
wrangler d1 migrations apply my-database --remote
通过这种方式,我们可以在本地快速迭代开发,同时确保生产环境的数据安全。
结束
而在下一章节中,就讲解Drizzle
,讲这个的主要目的是为了给大家普及一下海外批量应用
的基础套件的知识,欢迎加群。