
Cloudflare D1 — Photo by Joshua Sortino on Unsplash
Cloudflare D1: ฐานข้อมูล SQL แบบ Serverless ฟรีสำหรับโปรเจกต์ขนาดเล็ก
ฐานข้อมูลที่ทรงพลังโดยไม่ต้องจัดการเซิร์ฟเวอร์หรือจ่ายค่าบริการ
1. ทำความเข้าใจ Cloudflare D1
Cloudflare D1 คืออะไร?
- นิยาม: D1 คือฐานข้อมูล SQL แบบ Serverless ที่พัฒนาโดย Cloudflare บนพื้นฐานของ SQLite
- ความสามารถหลัก:
- ฐานข้อมูล SQL ที่ไม่ต้องจัดการเซิร์ฟเวอร์
- ทำงานที่ขอบของเครือข่าย (Edge) ใกล้กับผู้ใช้
- รองรับคำสั่ง SQL มาตรฐาน
- เชื่อมต่อกับ Cloudflare Workers ได้อย่างไร้รอยต่อ
- ข้อดี: ฟรีสำหรับการใช้งานพื้นฐาน ไม่มีค่าใช้จ่ายซ่อนเร้น
ทำไมต้องใช้ Cloudflare D1?
- ประหยัด: ไม่มีค่าใช้จ่ายสำหรับการใช้งานพื้นฐาน
- ง่าย: ไม่ต้องจัดการเซิร์ฟเวอร์หรือการตั้งค่าที่ซับซ้อน
- เร็ว: ทำงานที่ขอบของเครือข่าย ใกล้กับผู้ใช้
- ปลอดภัย: จัดการโดย Cloudflare ซึ่งเป็นผู้นำด้านความปลอดภัย
- ขยายได้: รองรับการเติบโตของแอปพลิเคชันโดยอัตโนมัติ
2. เปรียบเทียบ D1 กับฐานข้อมูลอื่น
D1 vs ฐานข้อมูล Serverless อื่นๆ
คุณสมบัติ | Cloudflare D1 | Firebase Firestore | AWS DynamoDB | PlanetScale |
---|---|---|---|---|
ประเภท | SQL (SQLite) | NoSQL | NoSQL | MySQL |
ราคาเริ่มต้น | ฟรี | ฟรี (มีข้อจำกัด) | ฟรี (มีข้อจำกัด) | ฟรี (มีข้อจำกัด) |
การเรียกใช้ | 100,000 ครั้ง/วัน (ฟรี) | 50,000 ครั้ง/วัน (ฟรี) | 25 RCU/WCU (ฟรี) | 1 GB storage, 5M rows read (ฟรี) |
ความเข้ากันได้กับ SQL | ✓ | ✗ | ✗ | ✓ |
การทำงานที่ Edge | ✓ | ✗ | ✗ | ✗ |
การจัดการ | ไม่ต้องจัดการ | ไม่ต้องจัดการ | ไม่ต้องจัดการ | ไม่ต้องจัดการ |
การเชื่อมต่อกับ Cloudflare | ไร้รอยต่อ | ต้องตั้งค่าเพิ่มเติม | ต้องตั้งค่าเพิ่มเติม | ต้องตั้งค่าเพิ่มเติม |
D1 vs ฐานข้อมูล SQL แบบดั้งเดิม
คุณสมบัติ | Cloudflare D1 | MySQL | PostgreSQL |
---|---|---|---|
ราคา | ฟรี (แผนพื้นฐาน) | ต้องจ่ายค่าเซิร์ฟเวอร์ | ต้องจ่ายค่าเซิร์ฟเวอร์ |
การจัดการ | ไม่ต้องจัดการ | ต้องจัดการเอง | ต้องจัดการเอง |
การขยาย | อัตโนมัติ | ต้องทำเอง | ต้องทำเอง |
ความสามารถ SQL | พื้นฐาน (SQLite) | สมบูรณ์ | สมบูรณ์ |
การทำงานที่ Edge | ✓ | ✗ | ✗ |
การสำรองข้อมูล | อัตโนมัติ | ต้องตั้งค่าเอง | ต้องตั้งค่าเอง |
การเชื่อมต่อ | ผ่าน Workers | ตรง | ตรง |
3. การเริ่มต้นใช้งาน Cloudflare D1
ขั้นตอนการสร้างฐานข้อมูลแรก
-
สร้างบัญชี Cloudflare
- ไปที่ cloudflare.com และสมัครบัญชีฟรี
- ยืนยันอีเมลของคุณ
-
ติดตั้ง Wrangler CLI
- Wrangler คือเครื่องมือสำหรับพัฒนา Cloudflare Workers และ D1
- ติดตั้งผ่าน npm:
npm install -g wrangler
- ล็อกอินเข้าสู่บัญชี Cloudflare:
wrangler login
-
สร้างฐานข้อมูล D1
- ใช้คำสั่ง:
wrangler d1 create <ชื่อฐานข้อมูล> # ตัวอย่าง wrangler d1 create my-database
- บันทึก database_id ที่ได้รับ
-
สร้างตาราง
- สร้างไฟล์ schema.sql:
CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, email TEXT UNIQUE NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP );
- นำเข้า schema:
wrangler d1 execute <ชื่อฐานข้อมูล> --file=./schema.sql
-
ทดสอบฐานข้อมูล
- เพิ่มข้อมูลทดสอบ:
wrangler d1 execute <ชื่อฐานข้อมูล> --command="INSERT INTO users (name, email) VALUES ('Test User', '[email protected]')"
- ดึงข้อมูล:
wrangler d1 execute <ชื่อฐานข้อมูล> --command="SELECT * FROM users"
การเชื่อมต่อ D1 กับ Cloudflare Workers
-
สร้างโปรเจกต์ Workers
wrangler init my-d1-project cd my-d1-project
-
ตั้งค่า D1 ใน wrangler.toml
[[d1_databases]] binding = "DB" # ชื่อที่จะใช้อ้างอิงในโค้ด database_name = "<ชื่อฐานข้อมูล>" database_id = "<database_id>"
-
เขียนโค้ด Worker ที่ใช้งาน D1
// src/index.js export default { async fetch(request, env) { const { pathname } = new URL(request.url); // API สำหรับดึงข้อมูลผู้ใช้ทั้งหมด if (pathname === "/api/users" && request.method === "GET") { const { results } = await env.DB.prepare("SELECT * FROM users").all(); return Response.json(results); } // API สำหรับเพิ่มผู้ใช้ใหม่ if (pathname === "/api/users" && request.method === "POST") { const { name, email } = await request.json(); if (!name || !email) { return new Response("Name and email are required", { status: 400 }); } try { const stmt = env.DB.prepare("INSERT INTO users (name, email) VALUES (?, ?)"); const result = await stmt.bind(name, email).run(); return Response.json({ success: true, id: result.lastRowId }); } catch (error) { return new Response(`Error: ${error.message}`, { status: 500 }); } } return new Response("Not found", { status: 404 }); } };
-
Deploy Worker
wrangler deploy
4. การใช้งาน D1 ในสถานการณ์จริง
การสร้างระบบสมาชิก
-
ออกแบบโครงสร้างฐานข้อมูล
-- schema.sql CREATE TABLE users ( id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT UNIQUE NOT NULL, email TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE sessions ( id TEXT PRIMARY KEY, user_id INTEGER NOT NULL, expires_at TEXT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) );
-
สร้าง API สำหรับลงทะเบียน
// ตัวอย่างโค้ดสำหรับการลงทะเบียน async function register(request, env) { const { username, email, password } = await request.json(); // ตรวจสอบข้อมูล if (!username || !email || !password) { return new Response("Missing required fields", { status: 400 }); } // ในสถานการณ์จริงควรเข้ารหัสรหัสผ่าน // นี่เป็นเพียงตัวอย่าง const password_hash = password; // ควรใช้ bcrypt หรือวิธีอื่นที่ปลอดภัย try { const stmt = env.DB.prepare( "INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)" ); const result = await stmt.bind(username, email, password_hash).run(); return Response.json({ success: true, user_id: result.lastRowId }); } catch (error) { return new Response(`Registration failed: ${error.message}`, { status: 500 }); } }
-
สร้าง API สำหรับเข้าสู่ระบบ
// ตัวอย่างโค้ดสำหรับการเข้าสู่ระบบ async function login(request, env) { const { email, password } = await request.json(); // ตรวจสอบข้อมูล if (!email || !password) { return new Response("Email and password are required", { status: 400 }); } try { // ค้นหาผู้ใช้ const user = await env.DB.prepare( "SELECT id, username, password_hash FROM users WHERE email = ?" ).bind(email).first(); if (!user || user.password_hash !== password) { return new Response("Invalid email or password", { status: 401 }); } // ตรวจสอบรหัสผ่านเสร็จสิ้น // สร้าง session const sessionId = crypto.randomUUID(); const expiresAt = new Date(); expiresAt.setDate(expiresAt.getDate() + 7); // หมดอายุใน 7 วัน await env.DB.prepare( "INSERT INTO sessions (id, user_id, expires_at) VALUES (?, ?, ?)" ).bind(sessionId, user.id, expiresAt.toISOString()).run(); return Response.json({ success: true, session_id: sessionId, user: { id: user.id, username: user.username } }); } catch (error) { return new Response(`Login failed: ${error.message}`, { status: 500 }); } }
การสร้างระบบบล็อกอย่างง่าย
-
ออกแบบโครงสร้างฐานข้อมูล
-- schema.sql CREATE TABLE posts ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, content TEXT NOT NULL, author_id INTEGER NOT NULL, published_at TEXT DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (author_id) REFERENCES users(id) ); CREATE TABLE comments ( id INTEGER PRIMARY KEY AUTOINCREMENT, post_id INTEGER NOT NULL, author_id INTEGER NOT NULL, content TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (post_id) REFERENCES posts(id), FOREIGN KEY (author_id) REFERENCES users(id) );
-
สร้าง API สำหรับจัดการบทความ
// ดึงบทความทั้งหมด async function getPosts(env) { const { results } = await env.DB.prepare(` SELECT p.id, p.title, p.published_at, u.username as author FROM posts p JOIN users u ON p.author_id = u.id ORDER BY p.published_at DESC `).all(); return Response.json(results); } // ดึงบทความเดียว async function getPost(postId, env) { // ดึงข้อมูลบทความ const post = await env.DB.prepare(` SELECT p.id, p.title, p.content, p.published_at, u.username as author FROM posts p JOIN users u ON p.author_id = u.id WHERE p.id = ? `).bind(postId).first(); if (!post) { return new Response("Post not found", { status: 404 }); } // ดึงความคิดเห็น const { results: comments } = await env.DB.prepare(` SELECT c.id, c.content, c.created_at, u.username as author FROM comments c JOIN users u ON c.author_id = u.id WHERE c.post_id = ? ORDER BY c.created_at ASC `).bind(postId).all(); // รวมข้อมูล post.comments = comments; return Response.json(post); } // สร้างบทความใหม่ async function createPost(request, env) { const { title, content, author_id } = await request.json(); if (!title || !content || !author_id) { return new Response("Missing required fields", { status: 400 }); } try { const result = await env.DB.prepare( "INSERT INTO posts (title, content, author_id) VALUES (?, ?, ?)" ).bind(title, content, author_id).run(); return Response.json({ success: true, post_id: result.lastRowId }); } catch (error) { return new Response(`Failed to create post: ${error.message}`, { status: 500 }); } }
การสร้างระบบ E-commerce อย่างง่าย
-
ออกแบบโครงสร้างฐานข้อมูล
-- schema.sql CREATE TABLE products ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, description TEXT, price REAL NOT NULL, stock INTEGER NOT NULL DEFAULT 0, image_url TEXT, created_at TEXT DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE orders ( id INTEGER PRIMARY KEY AUTOINCREMENT, user_id INTEGER NOT NULL, status TEXT NOT NULL DEFAULT 'pending', total REAL NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES users(id) ); CREATE TABLE order_items ( id INTEGER PRIMARY KEY AUTOINCREMENT, order_id INTEGER NOT NULL, product_id INTEGER NOT NULL, quantity INTEGER NOT NULL, price REAL NOT NULL, FOREIGN KEY (order_id) REFERENCES orders(id), FOREIGN KEY (product_id) REFERENCES products(id) );
-
สร้าง API สำหรับจัดการสินค้า
// ดึงสินค้าทั้งหมด async function getProducts(env) { const { results } = await env.DB.prepare( "SELECT * FROM products WHERE stock > 0" ).all(); return Response.json(results); } // ดึงข้อมูลสินค้าเดียว async function getProduct(productId, env) { const product = await env.DB.prepare( "SELECT * FROM products WHERE id = ?" ).bind(productId).first(); if (!product) { return new Response("Product not found", { status: 404 }); } return Response.json(product); }
-
สร้าง API สำหรับการสั่งซื้อ
// สร้างคำสั่งซื้อใหม่ async function createOrder(request, env) { const { user_id, items } = await request.json(); if (!user_id || !items || !items.length) { return new Response("Invalid order data", { status: 400 }); } // เริ่ม transaction const db = env.DB; try { // คำนวณยอดรวม let total = 0; for (const item of items) { const product = await db.prepare( "SELECT price, stock FROM products WHERE id = ?" ).bind(item.product_id).first(); if (!product) { return new Response(`Product ${item.product_id} not found`, { status: 404 }); } if (product.stock < item.quantity) { return new Response(`Not enough stock for product ${item.product_id}`, { status: 400 }); } total += product.price * item.quantity; } // สร้างคำสั่งซื้อ const orderResult = await db.prepare( "INSERT INTO orders (user_id, total) VALUES (?, ?)" ).bind(user_id, total).run(); const order_id = orderResult.lastRowId; // เพิ่มรายการสินค้า for (const item of items) { const product = await db.prepare( "SELECT price FROM products WHERE id = ?" ).bind(item.product_id).first(); // เพิ่มรายการในคำสั่งซื้อ await db.prepare( "INSERT INTO order_items (order_id, product_id, quantity, price) VALUES (?, ?, ?, ?)" ).bind(order_id, item.product_id, item.quantity, product.price).run(); // อัพเดทสต็อก await db.prepare( "UPDATE products SET stock = stock - ? WHERE id = ?" ).bind(item.quantity, item.product_id).run(); } return Response.json({ success: true, order_id, total }); } catch (error) { return new Response(`Order creation failed: ${error.message}`, { status: 500 }); } }
5. เทคนิคขั้นสูงในการใช้งาน D1
การใช้ Transactions
D1 รองรับ transactions ซึ่งช่วยให้คุณสามารถทำการเปลี่ยนแปลงหลายอย่างพร้อมกันได้อย่างปลอดภัย:
// ตัวอย่างการใช้ transaction
async function transferFunds(fromAccountId, toAccountId, amount, env) {
// เริ่ม transaction
const db = env.DB;
try {
// ตรวจสอบยอดเงิน
const fromAccount = await db.prepare(
"SELECT balance FROM accounts WHERE id = ?"
).bind(fromAccountId).first();
if (!fromAccount || fromAccount.balance < amount) {
return new Response("Insufficient funds", { status: 400 });
}
// หักเงินจากบัญชีต้นทาง
await db.prepare(
"UPDATE accounts SET balance = balance - ? WHERE id = ?"
).bind(amount, fromAccountId).run();
// เพิ่มเงินในบัญชีปลายทาง
await db.prepare(
"UPDATE accounts SET balance = balance + ? WHERE id = ?"
).bind(amount, toAccountId).run();
// บันทึกประวัติการโอน
await db.prepare(
"INSERT INTO transactions (from_account, to_account, amount) VALUES (?, ?, ?)"
).bind(fromAccountId, toAccountId, amount).run();
return Response.json({ success: true });
} catch (error) {
// หากเกิดข้อผิดพลาด transaction จะถูกยกเลิกโดยอัตโนมัติ
return new Response(`Transaction failed: ${error.message}`, { status: 500 });
}
}
การใช้ Prepared Statements
การใช้ Prepared Statements ช่วยป้องกัน SQL Injection และเพิ่มประสิทธิภาพ:
// ตัวอย่างการใช้ Prepared Statements
async function searchUsers(query, env) {
// ใช้ parameterized query เพื่อป้องกัน SQL Injection
const searchTerm = `%${query}%`;
const { results } = await env.DB.prepare(`
SELECT id, username, email
FROM users
WHERE username LIKE ? OR email LIKE ?
`).bind(searchTerm, searchTerm).all();
return Response.json(results);
}
การจัดการข้อมูลขนาดใหญ่
เมื่อต้องการดึงข้อมูลจำนวนมาก ควรใช้การแบ่งหน้า (pagination):
// ตัวอย่างการทำ pagination
async function getProductsPaginated(page, pageSize, env) {
const offset = (page - 1) * pageSize;
// ดึงข้อมูลตามหน้า
const { results: products } = await env.DB.prepare(`
SELECT * FROM products
ORDER BY created_at DESC
LIMIT ? OFFSET ?
`).bind(pageSize, offset).all();
// ดึงจำนวนทั้งหมด
const totalCount = await env.DB.prepare(
"SELECT COUNT(*) as count FROM products"
).first();
return Response.json({
products,
pagination: {
page,
pageSize,
totalItems: totalCount.count,
totalPages: Math.ceil(totalCount.count / pageSize)
}
});
}
6. การสำรองและนำเข้าข้อมูล
การสำรองข้อมูล (Backup)
D1 มีระบบสำรองข้อมูลอัตโนมัติ แต่คุณสามารถสำรองข้อมูลเองได้ด้วย Wrangler:
# สำรองฐานข้อมูลเป็นไฟล์ SQL
wrangler d1 export <ชื่อฐานข้อมูล> > backup.sql
การนำเข้าข้อมูล (Import)
คุณสามารถนำเข้าข้อมูลจากไฟล์ SQL ได้:
# นำเข้าข้อมูลจากไฟล์ SQL
wrangler d1 execute <ชื่อฐานข้อมูล> --file=./backup.sql
การโยกย้ายข้อมูลจากฐานข้อมูลอื่น
หากต้องการย้ายข้อมูลจากฐานข้อมูลอื่นมายัง D1:
-
ส่งออกข้อมูลจากฐานข้อมูลเดิม
- สำหรับ SQLite:
sqlite3 old_database.db .dump > export.sql
- สำหรับ MySQL:
mysqldump -u username -p database_name > export.sql
- สำหรับ PostgreSQL:
pg_dump -U username database_name > export.sql
- สำหรับ SQLite:
-
ปรับแต่งไฟล์ SQL ให้เข้ากับ D1
- D1 ใช้ SQLite เป็นพื้นฐาน ดังนั้นอาจต้องปรับแต่งคำสั่ง SQL ให้เข้ากับ SQLite
-
นำเข้าข้อมูลสู่ D1
wrangler d1 execute <ชื่อฐานข้อมูล> --file=./export.sql
7. การใช้ D1 ร่วมกับเฟรมเวิร์คยอดนิยม
การใช้ D1 กับ React
-
สร้าง API ด้วย Cloudflare Workers
// src/index.js (Worker) export default { async fetch(request, env) { // ตั้งค่า CORS const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, PUT, DELETE", "Access-Control-Allow-Headers": "Content-Type" }; // จัดการ OPTIONS request (preflight) if (request.method === "OPTIONS") { return new Response(null, { headers: corsHeaders }); } // เพิ่ม CORS headers ในทุก response const addCorsHeaders = (response) => { const newHeaders = new Headers(response.headers); Object.entries(corsHeaders).forEach(([key, value]) => { newHeaders.set(key, value); }); return new Response(response.body, { status: response.status, statusText: response.statusText, headers: newHeaders }); }; // จัดการ API routes const url = new URL(request.url); try { if (url.pathname === "/api/products" && request.method === "GET") { const { results } = await env.DB.prepare("SELECT * FROM products").all(); return addCorsHeaders(Response.json(results)); } // เพิ่ม routes อื่นๆ ตามต้องการ return addCorsHeaders(new Response("Not found", { status: 404 })); } catch (error) { return addCorsHeaders(new Response(`Error: ${error.message}`, { status: 500 })); } } };
-
เรียกใช้ API จาก React
// React component import { useState, useEffect } from 'react'; function ProductList() { const [products, setProducts] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { async function fetchProducts() { try { const response = await fetch('https://your-worker.your-subdomain.workers.dev/api/products'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); setProducts(data); } catch (e) { setError(e.message); } finally { setLoading(false); } } fetchProducts(); }, []); if (loading) return <p>Loading products...</p>; if (error) return <p>Error loading products: {error}</p>; return ( <div> <h2>Products</h2> <ul> {products.map(product => ( <li key={product.id}> <h3>{product.name}</h3> <p>{product.description}</p> <p>${product.price}</p> </li> ))} </ul> </div> ); } export default ProductList;
การใช้ D1 กับ Next.js
-
สร้าง API Routes ใน Next.js
// pages/api/products.js export default async function handler(req, res) { try { const response = await fetch('https://your-worker.your-subdomain.workers.dev/api/products'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); res.status(200).json(data); } catch (error) { res.status(500).json({ error: error.message }); } }
-
ใช้ SWR หรือ React Query เพื่อดึงข้อมูล
// components/ProductList.js import useSWR from 'swr'; const fetcher = (url) => fetch(url).then((res) => res.json()); function ProductList() { const { data, error, isLoading } = useSWR('/api/products', fetcher); if (isLoading) return <p>Loading products...</p>; if (error) return <p>Error loading products</p>; return ( <div> <h2>Products</h2> <ul> {data.map(product => ( <li key={product.id}> <h3>{product.name}</h3> <p>{product.description}</p> <p>${product.price}</p> </li> ))} </ul> </div> ); } export default ProductList;
8. ข้อจำกัดและการแก้ไขปัญหา
ข้อจำกัดของ D1
- ขนาดฐานข้อมูล: แผนฟรีมีขีดจำกัดขนาดฐานข้อมูล
- จำนวนการเรียกใช้: 100,000 ครั้งต่อวันในแผนฟรี
- ความสามารถ SQL: รองรับเฉพาะคำสั่ง SQLite ไม่รองรับฟีเจอร์ขั้นสูงของ MySQL หรือ PostgreSQL
- การเชื่อมต่อ: เข้าถึงได้เฉพาะผ่าน Cloudflare Workers เท่านั้น
- การทำงานแบบ Real-time: ไม่รองรับการทำงานแบบ real-time หรือ subscriptions
ปัญหาที่พบบ่อยและวิธีแก้ไข
-
การเชื่อมต่อล้มเหลว
- สาเหตุ: การตั้งค่า binding ไม่ถูกต้องใน wrangler.toml
- วิธีแก้ไข: ตรวจสอบ database_id และ binding name ให้ถูกต้อง
-
ข้อผิดพลาด SQL
- สาเหตุ: คำสั่ง SQL ไม่ถูกต้องหรือไม่รองรับใน SQLite
- วิธีแก้ไข: ตรวจสอบ syntax และปรับให้เข้ากับ SQLite
-
ประสิทธิภาพช้า
- สาเหตุ: ไม่มีการใช้ indexes หรือคำสั่ง SQL ไม่มีประสิทธิภาพ
- วิธีแก้ไข: เพิ่ม indexes และปรับปรุงคำสั่ง SQL
-
ข้อจำกัดการเรียกใช้
- สาเหตุ: เกินขีดจำกัดการเรียกใช้ในแผนฟรี
- วิธีแก้ไข: ใช้ caching หรือพิจารณาอัพเกรดแผน
การ Debug
-
ใช้ Wrangler เพื่อทดสอบ SQL
wrangler d1 execute <ชื่อฐานข้อมูล> --command="SELECT * FROM users LIMIT 10"
-
ใช้ Local Development
wrangler dev --local
-
ตรวจสอบ Logs
wrangler tail
9. กรณีศึกษา: การใช้งานจริงของ Startup
กรณีศึกษา 1: แอปพลิเคชันจัดการงาน
- ความท้าทาย: Startup ต้องการสร้างแอปจัดการงานที่ประหยัดต้นทุนแต่มีประสิทธิภาพ
- การใช้ D1:
- สร้างฐานข้อมูลสำหรับเก็บข้อมูลผู้ใช้ โปรเจกต์ และงาน
- ใช้ Cloudflare Workers เป็น API backend
- ใช้ React เป็น frontend
- ผลลัพธ์:
- ประหยัดค่าใช้จ่ายได้ 100% เมื่อเทียบกับการใช้ฐานข้อมูลแบบดั้งเดิม
- รองรับผู้ใช้ได้มากกว่า 1,000 คนโดยไม่มีปัญหาด้านประสิทธิภาพ
- ลดเวลาในการบำรุงรักษาระบบลง 90%
กรณีศึกษา 2: เว็บไซต์ E-commerce ขนาดเล็ก
- ความท้าทาย: ร้านค้าออนไลน์ขนาดเล็กต้องการระบบจัดการสินค้าและคำสั่งซื้อที่ประหยัด
- การใช้ D1:
- สร้างฐานข้อมูลสำหรับสินค้า คำสั่งซื้อ และลูกค้า
- ใช้ Cloudflare Workers เป็น API
- ใช้ Cloudflare Pages เป็น frontend
- ผลลัพธ์:
- ลดต้นทุนการดำเนินงานลง 80%
- เว็บไซต์โหลดเร็วขึ้น 40% เนื่องจากฐานข้อมูลทำงานที่ Edge
- รองรับช่วงการขายพิเศษที่มีทราฟฟิกสูงได้โดยไม่มีปัญหา
กรณีศึกษา 3: แพลตฟอร์มบล็อก
- ความท้าทาย: นักเขียนต้องการแพลตฟอร์มบล็อกที่มีประสิทธิภาพแต่ไม่มีค่าใช้จ่าย
- การใช้ D1:
- สร้างฐานข้อมูลสำหรับบทความ ความคิดเห็น และผู้ใช้
- ใช้ Markdown สำหรับเนื้อหาบทความ
- ใช้ Cloudflare Workers และ Pages
- ผลลัพธ์:
- ประหยัดค่าใช้จ่ายได้ 100% เมื่อเทียบกับแพลตฟอร์มบล็อกแบบมีค่าใช้จ่าย
- รองรับผู้อ่านได้มากกว่า 10,000 คนต่อวันโดยไม่มีปัญหา
- ลดเวลาในการโหลดหน้าเว็บลง 60%
10. เมื่อไรควรอัพเกรดและทางเลือกอื่น
สัญญาณที่ควรพิจารณาอัพเกรด
-
ปริมาณการใช้งาน:
- จำนวนการเรียกใช้เกิน 100,000 ครั้งต่อวัน
- ขนาดฐานข้อมูลเติบโตใกล้ขีดจำกัด
-
ความต้องการเพิ่มเติม:
- ต้องการฟีเจอร์ขั้นสูงที่ไม่มีใน SQLite
- ต้องการการรองรับและ SLA ระดับองค์กร
-
การเติบโตของธุรกิจ:
- จำนวนผู้ใช้เพิ่มขึ้นอย่างรวดเร็ว
- ต้องการความน่าเชื่อถือสูงขึ้น
ทางเลือกอื่นเมื่อเติบโตขึ้น
-
Cloudflare D1 แผนพรีเมียม:
- ยังคงใช้ D1 แต่อัพเกรดเป็นแผนพรีเมียม
- ได้รับขีดจำกัดที่สูงขึ้นและการรองรับเพิ่มเติม
-
ฐานข้อมูล SQL แบบจัดการ:
- MySQL หรือ PostgreSQL บน AWS RDS, Google Cloud SQL
- ได้รับความสามารถ SQL เต็มรูปแบบและการปรับแต่งมากขึ้น
-
ฐานข้อมูล NoSQL:
- MongoDB Atlas, Firebase Firestore
- เหมาะสำหรับข้อมูลที่ไม่มีโครงสร้างแน่นอนและการขยายตัวแนวนอน
-
ทางเลือกแบบ Serverless อื่นๆ:
- PlanetScale (MySQL แบบ serverless)
- Fauna (ฐานข้อมูล serverless แบบ global)
- Supabase (PostgreSQL แบบจัดการ)
11. สรุป: ทำไม D1 ถึงเหมาะกับ Startup
-
ประหยัดต้นทุน
ฟรีสำหรับการใช้งานพื้นฐาน ไม่มีค่าใช้จ่ายซ่อนเร้น -
ไม่ต้องจัดการเซิร์ฟเวอร์
ลดภาระในการดูแลระบบ ทีมสามารถโฟกัสที่การพัฒนาผลิตภัณฑ์ -
ประสิทธิภาพสูง
ทำงานที่ Edge ใกล้กับผู้ใช้ ทำให้แอปพลิเคชันตอบสนองเร็ว -
ใช้งานง่าย
ใช้ SQL มาตรฐานที่นักพัฒนาคุ้นเคย ไม่ต้องเรียนรู้เทคโนโลยีใหม่ -
เติบโตได้ตามต้องการ
เริ่มต้นด้วยแผนฟรี และอัพเกรดเมื่อธุรกิจเติบโตขึ้น
“Cloudflare D1 เป็นทางเลือกที่ยอดเยี่ยมสำหรับ Startup ที่ต้องการฐานข้อมูลที่มีประสิทธิภาพโดยไม่ต้องกังวลเรื่องค่าใช้จ่ายหรือการจัดการระบบ ช่วยให้ทีมสามารถโฟกัสที่การสร้างผลิตภัณฑ์ที่ดีแทนที่จะเสียเวลากับการดูแลโครงสร้างพื้นฐาน”
12. แหล่งเรียนรู้เพิ่มเติม
เอกสารและบทความ:
คอมมูนิตี้:
คอร์สและวิดีโอ:
เคล็ดลับ: Cloudflare มีการอัพเดทฟีเจอร์ใหม่ๆ สำหรับ D1 อยู่เสมอ ติดตาม Cloudflare Blog และ Twitter เพื่อไม่พลาดฟีเจอร์ล่าสุดที่อาจช่วยให้การใช้งานฐานข้อมูลของคุณดียิ่งขึ้น