Cloudflare D1 — Serverless SQL Database chạy trên Edge
Posted on: 4/26/2026 2:14:44 PM
Table of contents
- 1. Tại sao cần Cloudflare D1?
- 2. Kiến trúc bên trong D1
- 3. Tích hợp với Cloudflare Workers
- 4. Global Read Replication — Sessions API
- 5. Time Travel — Point-in-Time Recovery
- 6. Schema Migration và Computed Columns
- 7. ORM và Type Safety — Drizzle, Prisma
- 8. Multi-tenant Architecture — Database per Tenant
- 9. Chi phí — Scale to Zero thực sự
- 10. Giới hạn và khi nào KHÔNG nên dùng D1
- 11. D1 trong hệ sinh thái Cloudflare
- 12. Kết luận
Khi ứng dụng web cần database nhưng bạn không muốn quản lý server, lo scaling, hay trả phí khi không có traffic — Cloudflare D1 là câu trả lời. Đây là serverless SQL database được xây dựng trên nền SQLite, chạy trực tiếp trên mạng lưới edge toàn cầu của Cloudflare. Không cần provision, không connection pooling, không cold start đáng kể — chỉ cần viết SQL và deploy. Bài viết này phân tích sâu kiến trúc bên trong D1, cách tích hợp với Workers, chiến lược read replication toàn cầu, và khi nào nên (hoặc không nên) chọn D1 cho dự án của bạn.
1. Tại sao cần Cloudflare D1?
Trước D1, nếu bạn xây ứng dụng trên Cloudflare Workers (serverless edge functions), bạn có 2 lựa chọn lưu trữ dữ liệu có cấu trúc:
| Giải pháp | Ưu điểm | Nhược điểm |
|---|---|---|
| Workers KV | Key-value nhanh, eventually consistent, edge-native | Không có SQL, không query phức tạp, không JOIN |
| External DB (PlanetScale, Supabase, Neon...) | SQL đầy đủ, mature ecosystem | Latency cao (round-trip từ edge đến DB region), cần connection pooling, egress fee |
| Durable Objects | Strong consistency, transactional, edge-native | Mỗi object là single-point, không query across objects |
D1 lấp khoảng trống giữa KV (quá đơn giản) và external DB (quá xa). Nó mang SQL đầy đủ lên edge, với latency thấp và chi phí gần bằng 0 khi idle.
graph LR
subgraph "Trước D1"
W1["Workers"] -->|"High latency"| EDB["External DB
us-east-1"]
W1 -->|"Key-value only"| KV["Workers KV"]
end
subgraph "Với D1"
W2["Workers"] -->|"~0ms binding"| D1["D1 Database
SQLite on Edge"]
D1 -->|"Auto replicate"| R1["Read Replica
Asia"]
D1 -->|"Auto replicate"| R2["Read Replica
Europe"]
end
style D1 fill:#e94560,stroke:#fff,color:#fff
style W2 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style EDB fill:#f8f9fa,stroke:#e0e0e0,color:#888
style R1 fill:#4CAF50,stroke:#fff,color:#fff
style R2 fill:#4CAF50,stroke:#fff,color:#fff
Hình 1: D1 đưa SQL database trực tiếp lên edge — loại bỏ round-trip đến DB region xa
2. Kiến trúc bên trong D1
D1 được xây dựng trên 2 nền tảng cốt lõi của Cloudflare:
2.1. SQLite làm SQL Engine
Thay vì xây database engine từ đầu, Cloudflare chọn SQLite — database nhúng phổ biến nhất thế giới với hơn 1 nghìn tỷ (trillion) database đang hoạt động. SQLite mang lại:
- SQL đầy đủ: JOIN, subquery, window functions, CTE, JSON functions, full-text search (FTS5)
- Zero config: Không cần tuning buffer pool, shared memory, hay connection limits
- Single-file storage: Toàn bộ database là 1 file — dễ backup, replicate, và migrate
- Battle-tested: Hơn 20 năm phát triển, được dùng trong mọi smartphone, browser, và hệ điều hành
2.2. Durable Objects làm Coordination Layer
Mỗi D1 database được host bởi một Durable Object — đảm bảo tại bất kỳ thời điểm nào, chỉ có đúng 1 instance của database đang chạy trên toàn cầu. Điều này giải quyết bài toán consistency mà không cần distributed consensus protocol phức tạp:
graph TB
subgraph "Global Cloudflare Network"
subgraph "Primary Region"
DO["Durable Object
(Single Writer)"]
DB["SQLite File
(WAL Mode)"]
DO --> DB
end
subgraph "Edge PoPs"
E1["PoP Singapore"]
E2["PoP Frankfurt"]
E3["PoP São Paulo"]
end
end
E1 -->|"Read/Write requests"| DO
E2 -->|"Read/Write requests"| DO
E3 -->|"Read/Write requests"| DO
style DO fill:#e94560,stroke:#fff,color:#fff
style DB fill:#2c3e50,stroke:#fff,color:#fff
style E1 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style E2 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style E3 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
Hình 2: D1 dùng Durable Object làm single-writer primary — đảm bảo snapshot isolation
Snapshot Isolation — cùng level với SQLite WAL mode
D1 cung cấp snapshot isolation — mức consistency tương đương SQLite chạy WAL (Write-Ahead Logging). Nghĩa là:
Readers không block writers: Nhiều read query có thể chạy đồng thời với write, mỗi reader thấy snapshot nhất quán tại thời điểm bắt đầu query.
Writers serialized: Chỉ 1 write transaction chạy tại 1 thời điểm (single-writer model), đảm bảo không có write conflicts.
Đây là trade-off có chủ đích: D1 chọn simplicity và correctness thay vì multi-writer phức tạp.
3. Tích hợp với Cloudflare Workers
D1 được thiết kế để tích hợp zero-config với Workers thông qua bindings — không cần connection string, không cần driver, không cần connection pooling:
# wrangler.toml — khai báo D1 binding
name = "my-app"
main = "src/index.ts"
compatibility_date = "2026-04-01"
[[d1_databases]]
binding = "DB"
database_name = "my-production-db"
database_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
// src/index.ts — Worker sử dụng D1
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const url = new URL(request.url);
if (url.pathname === "/api/products") {
// Prepared statement — tự động parameterized, chống SQL injection
const { results } = await env.DB.prepare(
"SELECT id, name, price FROM products WHERE category = ? ORDER BY created_at DESC LIMIT ?"
)
.bind("electronics", 20)
.all();
return Response.json(results);
}
if (url.pathname === "/api/products" && request.method === "POST") {
const body = await request.json();
// Batch operations — nhiều statement trong 1 round-trip
const statements = [
env.DB.prepare(
"INSERT INTO products (name, price, category) VALUES (?, ?, ?)"
).bind(body.name, body.price, body.category),
env.DB.prepare(
"INSERT INTO audit_log (action, entity, timestamp) VALUES (?, ?, ?)"
).bind("CREATE", "product", new Date().toISOString()),
];
const results = await env.DB.batch(statements);
return Response.json({ success: true, results });
}
return new Response("Not found", { status: 404 });
},
};
interface Env {
DB: D1Database;
}
Batch API — atomic multi-statement
env.DB.batch() gửi nhiều SQL statement trong 1 round-trip duy nhất đến database, và tất cả chạy trong cùng 1 implicit transaction. Nếu bất kỳ statement nào fail, toàn bộ batch sẽ rollback. Đây là cách hiệu quả nhất để thực hiện write operations phức tạp trên D1 mà không cần explicit transaction API.
3.1. Client API chi tiết
| Method | Mô tả | Use case |
|---|---|---|
.prepare(sql) | Tạo prepared statement với parameterized query | Mọi query — luôn dùng để chống SQL injection |
.bind(...params) | Bind giá trị vào placeholder ? | Truyền dynamic values an toàn |
.all() | Trả về tất cả rows + metadata (success, meta.changes, meta.duration) | SELECT queries trả nhiều rows |
.first(column?) | Trả về row đầu tiên, hoặc giá trị 1 column cụ thể | SELECT ... LIMIT 1, COUNT(*), etc. |
.run() | Execute statement không trả rows (INSERT, UPDATE, DELETE) | Write operations |
.raw() | Trả rows dưới dạng array thay vì object | Khi cần performance tối đa, bỏ overhead column mapping |
.batch(stmts[]) | Execute nhiều statements trong 1 transaction | Multi-write operations atomic |
4. Global Read Replication — Sessions API
Hạn chế lớn nhất của kiến trúc single-writer: nếu primary database ở US, user ở Việt Nam phải chịu ~200ms latency cho mỗi query. Sessions API giải quyết bằng cách tạo read replicas tự động gần user:
sequenceDiagram
participant User as User (Vietnam)
participant Edge as Edge PoP (Singapore)
participant Replica as Read Replica (Singapore)
participant Primary as Primary DB (US)
User->>Edge: SELECT query
Edge->>Replica: Read from local replica
Replica-->>Edge: Result (< 5ms)
Edge-->>User: Fast response
User->>Edge: INSERT/UPDATE query
Edge->>Primary: Route to primary
Primary-->>Edge: Write confirmed + commit token
Edge-->>User: Response + new commit token
Note over Edge,Replica: Replica nhận async replication
Note over User,Edge: Next read dùng commit token
để đảm bảo sequential consistency
Hình 3: Sessions API — reads từ replica gần nhất, writes route đến primary
4.1. Commit Token — đảm bảo Sequential Consistency
Vấn đề kinh điển của async replication: read-after-write inconsistency. User vừa INSERT 1 row, nhưng read tiếp theo đi đến replica chưa kịp sync → không thấy data vừa ghi. D1 giải quyết bằng commit tokens (Lamport timestamps):
// Sử dụng Sessions API với commit token
export default {
async fetch(request: Request, env: Env): Promise<Response> {
// Lấy commit token từ cookie hoặc header
const token = request.headers.get("x-d1-token") || "";
// Tạo session với token — D1 đảm bảo replica đã sync đến ít nhất token này
const session = env.DB.withSession(token);
if (request.method === "POST") {
// Write luôn đi đến primary
const result = await session
.prepare("INSERT INTO posts (title, body) VALUES (?, ?)")
.bind("Hello", "World")
.run();
// Trả về commit token mới — client gửi lại ở request tiếp theo
return Response.json(
{ success: true },
{ headers: { "x-d1-token": result.meta.token } }
);
}
// Read có thể đi đến replica gần nhất (nếu đã sync đến token)
const { results } = await session
.prepare("SELECT * FROM posts ORDER BY id DESC LIMIT 10")
.all();
return Response.json(results);
},
};
Sequential Consistency — không phải Strong Consistency
Sessions API đảm bảo sequential consistency — mọi operation của cùng 1 session sẽ thấy kết quả theo đúng thứ tự thực hiện. Nhưng giữa các sessions khác nhau, có thể thấy state khác nhau (stale reads chấp nhận được).
Trong thực tế: user A vừa post comment, user A sẽ thấy comment ngay lập tức (nhờ commit token). User B ở châu Âu có thể thấy comment sau vài trăm millisecond khi replica sync xong. Đối với hầu hết ứng dụng web, đây là trade-off hoàn toàn chấp nhận được.
5. Time Travel — Point-in-Time Recovery
D1 tự động backup mỗi phút (không cần config) và cho phép restore database về bất kỳ thời điểm nào trong 30 ngày gần nhất:
# Xem danh sách bookmarks (named restore points)
npx wrangler d1 time-travel info my-production-db
# Restore về 1 thời điểm cụ thể
npx wrangler d1 time-travel restore my-production-db \
--timestamp "2026-04-25T10:30:00Z"
# Hoặc restore về 1 bookmark
npx wrangler d1 time-travel restore my-production-db \
--bookmark "before-migration-v42"
# Tạo bookmark trước khi chạy migration nguy hiểm
npx wrangler d1 time-travel bookmark my-production-db \
--name "before-migration-v42"
Dùng Time Travel để debug production
Một use case mạnh: tải snapshot production về local để debug. Thay vì reproduce bug trên staging (thường khác data production), bạn có thể pull chính xác state của database tại thời điểm bug xảy ra, query trực tiếp trên máy local bằng SQLite client bất kỳ.
6. Schema Migration và Computed Columns
6.1. Migration với Wrangler
# Tạo migration file
npx wrangler d1 migrations create my-db add-user-profiles
# File được tạo tại migrations/0001_add-user-profiles.sql
-- migrations/0001_add-user-profiles.sql
CREATE TABLE IF NOT EXISTS user_profiles (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id TEXT NOT NULL UNIQUE,
display_name TEXT NOT NULL,
bio TEXT,
avatar_url TEXT,
metadata JSON,
created_at TEXT DEFAULT (datetime('now')),
updated_at TEXT DEFAULT (datetime('now'))
);
CREATE INDEX idx_user_profiles_user_id ON user_profiles(user_id);
-- Computed column — tự động extract từ JSON
ALTER TABLE user_profiles
ADD COLUMN location TEXT
GENERATED ALWAYS AS (json_extract(metadata, '$.location')) STORED;
# Apply migration lên production (hoặc preview trước)
npx wrangler d1 migrations apply my-db --remote
# Xem status migrations
npx wrangler d1 migrations list my-db --remote
6.2. Computed Columns — tính toán tại database level
Computed columns cho phép D1 tự động tính giá trị từ các column khác hoặc từ JSON data. Đặc biệt hữu ích khi lưu JSON flexible nhưng vẫn cần index và query nhanh:
-- Lưu settings dạng JSON, nhưng extract fields cần query thường xuyên
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
data JSON NOT NULL,
-- Computed columns — auto-extracted từ JSON
price REAL GENERATED ALWAYS AS (json_extract(data, '$.price')) STORED,
category TEXT GENERATED ALWAYS AS (json_extract(data, '$.category')) STORED,
in_stock INTEGER GENERATED ALWAYS AS (
CASE WHEN json_extract(data, '$.quantity') > 0 THEN 1 ELSE 0 END
) STORED
);
-- Index trên computed column — query nhanh như column thường
CREATE INDEX idx_products_category ON products(category);
CREATE INDEX idx_products_price ON products(price);
-- Query bình thường — không cần json_extract trong WHERE
SELECT name, price FROM products
WHERE category = 'electronics' AND in_stock = 1
ORDER BY price ASC;
STORED vs VIRTUAL computed columns
SQLite hỗ trợ 2 loại computed column:
STORED: Giá trị được tính và lưu vào disk khi INSERT/UPDATE. Tốn thêm storage nhưng SELECT nhanh hơn — D1 chỉ hỗ trợ STORED vì phù hợp hơn với read-heavy workload trên edge.
VIRTUAL: Giá trị được tính lại mỗi lần SELECT. Tiết kiệm storage nhưng tốn CPU khi query.
7. ORM và Type Safety — Drizzle, Prisma
Viết raw SQL hoạt động tốt cho project nhỏ, nhưng khi schema phức tạp, bạn cần ORM. D1 hỗ trợ first-class với 2 ORM phổ biến nhất:
7.1. Drizzle ORM — lightweight, type-safe
// schema.ts — define schema với Drizzle
import { sqliteTable, text, integer, real } from "drizzle-orm/sqlite-core";
export const products = sqliteTable("products", {
id: integer("id").primaryKey({ autoIncrement: true }),
name: text("name").notNull(),
price: real("price").notNull(),
category: text("category").notNull(),
createdAt: text("created_at").default("datetime('now')"),
});
export const orders = sqliteTable("orders", {
id: integer("id").primaryKey({ autoIncrement: true }),
productId: integer("product_id").references(() => products.id),
quantity: integer("quantity").notNull(),
total: real("total").notNull(),
});
// worker.ts — sử dụng Drizzle với D1
import { drizzle } from "drizzle-orm/d1";
import { eq, desc, and, gte } from "drizzle-orm";
import * as schema from "./schema";
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const db = drizzle(env.DB, { schema });
// Type-safe query — IDE autocomplete + compile-time check
const expensiveProducts = await db
.select({
name: schema.products.name,
price: schema.products.price,
})
.from(schema.products)
.where(
and(
eq(schema.products.category, "electronics"),
gte(schema.products.price, 100)
)
)
.orderBy(desc(schema.products.price))
.limit(10);
// JOIN query — type-safe
const ordersWithProducts = await db
.select({
orderId: schema.orders.id,
productName: schema.products.name,
quantity: schema.orders.quantity,
total: schema.orders.total,
})
.from(schema.orders)
.innerJoin(
schema.products,
eq(schema.orders.productId, schema.products.id)
);
return Response.json({ expensiveProducts, ordersWithProducts });
},
};
| Tiêu chí | Drizzle ORM | Prisma |
|---|---|---|
| Bundle size | ~45 KB (lightweight) | ~2 MB (Prisma Client + engine) |
| D1 support | Native adapter, zero overhead | Qua Prisma Accelerate hoặc driver adapter |
| Type safety | SQL-first — schema = TypeScript code | Schema-first — generate client từ .prisma file |
| Migration | drizzle-kit push — auto diff schema | prisma migrate — migration files |
| Raw SQL | Dễ dàng mix raw SQL khi cần | $queryRaw — kém type-safe hơn |
| Edge runtime | Chạy tốt trên Workers | Cần Prisma Accelerate (thêm hop) |
Khuyến nghị cho D1
Drizzle ORM là lựa chọn tốt hơn cho D1 vì bundle size nhỏ (quan trọng trên Workers có giới hạn 10 MB), native D1 adapter không qua proxy, và API gần với SQL thuần — dễ tối ưu query. Prisma phù hợp hơn nếu team đã quen Prisma ecosystem và chấp nhận overhead.
8. Multi-tenant Architecture — Database per Tenant
Một kiến trúc mà D1 excel: database-per-tenant. Với giới hạn 50.000 databases/account và không tính phí cho DB idle, bạn có thể tạo 1 database riêng cho mỗi user hoặc mỗi organization:
graph TB
subgraph "Traditional Multi-tenant"
APP1["App Server"] --> SHARED["Shared Database
tenant_id column everywhere
Complex RLS policies"]
end
subgraph "D1 Multi-tenant"
APP2["Worker"] --> ROUTER["Tenant Router"]
ROUTER --> DB1["D1: tenant-acme
10 MB"]
ROUTER --> DB2["D1: tenant-globex
50 MB"]
ROUTER --> DB3["D1: tenant-initech
5 MB"]
ROUTER --> DB4["D1: tenant-....
50,000 DBs max"]
end
style SHARED fill:#ff9800,stroke:#fff,color:#fff
style ROUTER fill:#e94560,stroke:#fff,color:#fff
style DB1 fill:#4CAF50,stroke:#fff,color:#fff
style DB2 fill:#4CAF50,stroke:#fff,color:#fff
style DB3 fill:#4CAF50,stroke:#fff,color:#fff
style DB4 fill:#4CAF50,stroke:#fff,color:#fff
Hình 4: D1 per-tenant — mỗi tenant 1 database riêng, isolation hoàn toàn
Ưu điểm của per-tenant database:
- Isolation hoàn toàn: Không cần
WHERE tenant_id = ?ở mọi query. Bug ở 1 tenant không ảnh hưởng tenant khác. - Compliance dễ hơn: Data residency (GDPR) — đặt DB của EU tenant ở EU region. Xóa tenant = xóa database, không cần
DELETE FROM ... WHERE tenant_id = ?ở 50 bảng. - Performance predictable: Tenant lớn không làm chậm tenant nhỏ. Mỗi DB có tài nguyên riêng.
- Schema flexibility: Có thể migrate schema dần dần (blue-green migration cho từng tenant).
// Tenant routing — lookup database binding dynamically
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const tenantId = getTenantFromRequest(request);
// D1 binding name phải biết lúc deploy, nhưng có thể dùng
// Cloudflare API để quản lý dynamic bindings
const dbName = `TENANT_DB_${tenantId}`;
const db = env[dbName] as D1Database;
if (!db) {
return new Response("Tenant not found", { status: 404 });
}
const { results } = await db
.prepare("SELECT * FROM settings")
.all();
return Response.json(results);
},
};
9. Chi phí — Scale to Zero thực sự
| Tier | Free (Workers Free) | Paid ($5/mo Workers Paid) |
|---|---|---|
| Reads | 5 triệu rows/ngày | 25 tỷ rows/tháng, sau đó $0.001/triệu rows |
| Writes | 100.000 rows/ngày | 50 triệu rows/tháng, sau đó $1.00/triệu rows |
| Storage | 5 GB (total across all DBs) | 5 GB included, sau đó $0.75/GB-month |
| Database limit | 500 databases | 50.000 databases |
| Max DB size | 10 GB | 10 GB |
| Egress fee | $0 | $0 |
So sánh chi phí với alternatives
Ví dụ 1 SaaS app với 10 triệu reads + 500K writes/tháng, 2 GB storage:
Cloudflare D1: $5/tháng (chỉ Workers Paid plan, reads + writes + storage đều trong free tier)
PlanetScale (Scaler Pro): ~$39/tháng
Neon (Scale): ~$69/tháng
Supabase (Pro): ~$25/tháng
D1 rẻ hơn đáng kể, nhưng nhớ rằng trade-off là: 10 GB limit, chỉ SQLite SQL, và phải chạy trên Workers.
10. Giới hạn và khi nào KHÔNG nên dùng D1
| Giới hạn | Chi tiết | Ảnh hưởng |
|---|---|---|
| Max DB size | 10 GB mỗi database | Không phù hợp cho data warehouse hoặc app có nhiều binary data |
| Max row size | ~2 MB (SQLite limit) | Không lưu large blob trực tiếp — dùng R2 cho files |
| Write latency | Phụ thuộc khoảng cách đến primary | Users xa primary region chịu latency cao cho writes |
| No stored procedures | SQLite không có stored procedures truyền thống | Logic phức tạp phải nằm trong Worker code |
| No real-time subscriptions | Không có change streams hay webhooks built-in | Cần Durable Objects hoặc polling cho real-time |
| Single-writer | Tất cả writes qua 1 primary | Write throughput có ceiling — không scale writes horizontally |
D1 KHÔNG phù hợp cho:
1. Data lớn: Analytics, logs, time-series data vượt 10 GB → dùng ClickHouse, BigQuery, hoặc Cloudflare Analytics Engine.
2. Write-heavy workloads: Hệ thống chat real-time, IoT sensors ghi hàng nghìn writes/giây → single-writer sẽ là bottleneck.
3. Complex queries cần PostgreSQL features: Nếu cần extensions (PostGIS, pg_trgm), advanced indexes (GIN, GiST), hoặc materialized views → PostgreSQL vẫn là vua.
4. Ứng dụng không chạy trên Cloudflare: D1 bindings chỉ hoạt động từ Workers/Pages. Nếu backend là .NET, Go, hay Python trên server riêng → cần HTTP API (chậm hơn binding).
5. Cần ACID cross-database transactions: Mỗi D1 database là independent — không có distributed transactions giữa 2 D1 databases.
11. D1 trong hệ sinh thái Cloudflare
graph TB
subgraph "Application Layer"
W["Workers / Pages Functions"]
end
subgraph "Storage Layer"
D1DB["D1
Relational SQL"]
KV["Workers KV
Key-Value, edge-cached"]
R2["R2
Object Storage (files, images)"]
DO["Durable Objects
Stateful coordination"]
VE["Vectorize
Vector embeddings"]
AE["Analytics Engine
Time-series, event logs"]
HY["Hyperdrive
Connection pool to external DBs"]
end
W --> D1DB
W --> KV
W --> R2
W --> DO
W --> VE
W --> AE
W --> HY
style D1DB fill:#e94560,stroke:#fff,color:#fff
style W fill:#2c3e50,stroke:#fff,color:#fff
style KV fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style R2 fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style DO fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style VE fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style AE fill:#f8f9fa,stroke:#e94560,color:#2c3e50
style HY fill:#f8f9fa,stroke:#e94560,color:#2c3e50
Hình 5: D1 là 1 phần trong hệ sinh thái storage của Cloudflare — mỗi service phục vụ use case khác nhau
| Cần gì? | Dùng gì? |
|---|---|
| Relational data, SQL queries, ACID transactions | D1 |
| Simple key-value lookups, configuration, session data | Workers KV |
| Files, images, videos, large binary objects | R2 |
| Real-time coordination, WebSocket state, rate limiting | Durable Objects |
| Semantic search, AI embeddings, similarity queries | Vectorize |
| Event logs, analytics, time-series (write-heavy) | Analytics Engine |
| Connect Workers đến PostgreSQL/MySQL bên ngoài | Hyperdrive |
12. Kết luận
Cloudflare D1 đại diện cho một hướng đi mới trong database: đưa SQL database lên edge, zero-config, scale-to-zero. Với nền tảng SQLite đã battle-tested, Durable Objects đảm bảo consistency, Sessions API cho global read replication, và pricing gần như miễn phí cho small-to-medium workloads — D1 là lựa chọn lý tưởng cho ứng dụng edge-first trên Cloudflare.
Tuy nhiên, D1 không phải universal database. Giới hạn 10 GB, single-writer model, và vendor lock-in với Cloudflare Workers là trade-offs quan trọng cần cân nhắc. Với hệ thống cần write throughput cao, data lớn, hoặc PostgreSQL-level features — hãy tiếp tục dùng managed PostgreSQL (Neon, Supabase) hoặc Hyperdrive để kết nối từ Workers đến external database. D1 tỏa sáng ở đúng niche của nó: ứng dụng edge-native, read-heavy, cần SQL, và muốn đơn giản tối đa.
Nguồn tham khảo
Vitest 4 — Testing Framework nhanh gấp 28 lần Jest cho Vue và Vite
Istio Ambient Mesh — Service Mesh Không Cần Sidecar, Giảm 70% Tài Nguyên
Disclaimer: The opinions expressed in this blog are solely my own and do not reflect the views or opinions of my employer or any affiliated organizations. The content provided is for informational and educational purposes only and should not be taken as professional advice. While I strive to provide accurate and up-to-date information, I make no warranties or guarantees about the completeness, reliability, or accuracy of the content. Readers are encouraged to verify the information and seek independent advice as needed. I disclaim any liability for decisions or actions taken based on the content of this blog.