PlanetScale Serverless Database: Migration Schema Không Downtime với Branching

Database tutorial - IT technology blog
Database tutorial - IT technology blog

Vấn Đề Migration Schema Mà Không Ai Nói Đến

Chạy ALTER TABLE trên bảng MySQL production với 10 triệu dòng dữ liệu là thứ khiến các backend engineer mất ngủ cả đêm. Tôi đã trải qua rồi — nhìn một migration khóa bảng suốt 40 phút trong khi kênh support ngập tràn khiếu nại. Sau khi làm việc với MySQL, PostgreSQL và MongoDB qua nhiều dự án khác nhau, mỗi cái có điểm mạnh riêng, nhưng câu chuyện migration schema của MySQL từ trước đến nay vẫn là đau đầu nhất trong ba cái.

Vấn đề cốt lõi: các DDL operation của MySQL có thể chiếm metadata lock khiến cả reads lẫn writes bị block. Dù có công cụ như pt-online-schema-change hay gh-ost, bạn vẫn phải xoay xở với shadow table, trigger và race condition. Với team nhỏ hay startup, sự phức tạp này là rào cản thực sự khi muốn triển khai thay đổi database một cách tự tin.

PlanetScale tiếp cận theo một hướng hoàn toàn khác. Nó xử lý các thay đổi schema database theo cách Git xử lý code — thông qua branch và merge request. Kết quả là những migration schema không-blocking, có thể review và có thể hoàn tác.

Các Khái Niệm Cốt Lõi Cần Nắm Trước

PlanetScale Thực Sự Là Gì

PlanetScale là một nền tảng database serverless tương thích MySQL, xây dựng trên nền tảng Vitess — hệ thống sharding và quản lý kết nối mà YouTube đã sử dụng từ năm 2011. Bạn được hưởng sự tương thích với MySQL wire protocol (ORM và driver hiện có của bạn dùng được ngay) cộng với DDL engine không-blocking của Vitess bên dưới.

Phần “serverless” có nghĩa là bạn không cần tự quản lý instance, replica hay giới hạn kết nối. Scaling được xử lý tự động, và bạn được tính phí dựa trên lưu trữ và số lần đọc/ghi row — không phải theo giờ.

Database Branching

Mỗi database PlanetScale có một production branch (được bảo vệ, không cho phép DDL trực tiếp) và bất kỳ số lượng development branch nào. Một branch là bản sao đầy đủ schema của bạn — không phải dữ liệu — vì vậy bạn có thể tự do chạy migration trong môi trường riêng mà không ảnh hưởng đến production.

Hãy nghĩ về nó như này:

  • main — production branch của bạn (schema chỉ thay đổi qua deploy request)
  • add-user-avatar — dev branch để thêm cột
  • refactor-indexes — dev branch khác chạy song song

Branch rất nhanh và dễ tạo. Bạn có thể có hàng chục branch chạy đồng thời mà không ảnh hưởng hiệu năng production.

Deploy Requests

Khi thay đổi schema sẵn sàng, bạn mở một deploy request — tương đương pull request của PlanetScale, nhưng dành cho database migration. Nó hiển thị diff chính xác những DDL sẽ chạy, để đồng đội review, rồi thực thi trên production bằng Online DDL engine của Vitess. Không lock bảng, không downtime.

Thay Đổi Schema Không Blocking

Chiến lược DDL của Vitess (vitess hoặc online) dùng cách tiếp cận shadow table tương tự gh-ost, nhưng được tích hợp sẵn vào nền tảng. Khi bạn deploy một thay đổi schema, Vitess:

  1. Tạo một shadow table mới với schema mong muốn
  2. Sao chép row theo từng batch mà không block reads hay writes
  3. Áp dụng các thay đổi liên tục thông qua binary log tailing
  4. Thực hiện atomic cutover khi đã bắt kịp

Toàn bộ quá trình là trong suốt. Ứng dụng của bạn tiếp tục phục vụ traffic trong suốt thời gian đó.

Thực Hành: Deploy Schema Migration Đầu Tiên

Bước 1 — Cài Đặt PlanetScale CLI

CLI pscale là cách bạn quản lý mọi thứ ở local. Cài đặt bằng package manager của bạn:

# macOS
brew install planetscale/tap/pscale

# Linux (Debian/Ubuntu)
curl -fsSL https://cli.planetscale.com/install.sh | bash

# Kiểm tra
pscale version

Bước 2 — Xác Thực và Tạo Database

# Mở trình duyệt để đăng nhập OAuth
pscale auth login

# Tạo database mới (có gói miễn phí)
pscale database create my-app-db --region us-east

# Kiểm tra trạng thái
pscale database show my-app-db

Database của bạn có sẵn một branch main để dùng ngay. Gói miễn phí cung cấp 5 GB lưu trữ và 1 tỷ lần đọc row mỗi tháng — quá đủ cho hầu hết side project hay môi trường staging.

Bước 3 — Kết Nối Local Qua Proxy

PlanetScale sử dụng mTLS cho tất cả kết nối. CLI proxy xử lý việc này cho bạn, mở ra một MySQL socket local mà app có thể kết nối bình thường:

# Kết nối với production branch
pscale connect my-app-db main --port 3309

# Kết nối bằng bất kỳ MySQL client nào
mysql -h 127.0.0.1 -P 3309 -u root

Với ứng dụng của bạn, hãy dùng connection string từ PlanetScale dashboard thay vì proxy — nó cung cấp hostname thực với thông tin xác thực.

Bước 4 — Tạo Development Branch Cho Thay Đổi Schema

Không bao giờ chạy DDL trực tiếp trên main. Luôn tạo branch trước:

# Tạo branch từ main
pscale branch create my-app-db add-user-avatar --from main

# Kết nối với dev branch
pscale connect my-app-db add-user-avatar --port 3310

Bây giờ chạy migration trên dev branch:

-- Kết nối với port 3310 và chạy DDL thoải mái
ALTER TABLE users ADD COLUMN avatar_url VARCHAR(500) DEFAULT NULL;
CREATE INDEX idx_users_created_at ON users (created_at);
ALTER TABLE posts ADD COLUMN reading_time_minutes TINYINT UNSIGNED DEFAULT 0;

Các câu lệnh DDL này chạy tức thì trên dev branch vì không có dữ liệu production — bạn chỉ đang sửa đổi định nghĩa schema.

Bước 5 — Mở Deploy Request

# Tạo deploy request (branch → main)
pscale deploy-request create my-app-db add-user-avatar

Bạn cũng có thể làm điều này từ web UI, nơi hiển thị diff rõ ràng của các thay đổi schema. Chia sẻ link deploy request với team để review. Sau khi được phê duyệt:

# Lấy số thứ tự deploy request
pscale deploy-request list my-app-db

# Deploy nó
pscale deploy-request deploy my-app-db 1

PlanetScale xếp hàng migration và chạy bằng Online DDL. Với bảng có hàng triệu row, quá trình này có thể mất vài phút chạy ngầm — nhưng ứng dụng của bạn không bị gián đoạn chút nào.

Bước 6 — Theo Dõi và Rollback Nếu Cần

# Xem tiến độ migration
pscale deploy-request show my-app-db 1

# Nếu có vấn đề, hoàn tác
pscale deploy-request revert my-app-db 1

Thao tác revert xóa cột hoặc index đã thêm bằng cùng một quy trình không-blocking. Tôi đã dùng tính năng này hai lần trong production — một lần khi index mới gây ra query plan regression bất ngờ, và một lần khi giá trị mặc định của cột bị sai. Cả hai lần revert đều hoàn thành mà không có downtime.

Kết Nối Từ Ứng Dụng

Với ứng dụng Node.js/TypeScript dùng Prisma, kết nối là MySQL chuẩn:

# .env — lấy các giá trị này từ PlanetScale dashboard → Connect → Prisma
DATABASE_URL="mysql://user:[email protected]/my-app-db?sslaccept=strict"
# Python với SQLAlchemy
import os
from sqlalchemy import create_engine

engine = create_engine(
    os.environ["DATABASE_URL"],
    connect_args={"ssl": {"ca": "/etc/ssl/certs/ca-certificates.crt"}}
)

Kinh Nghiệm Từ Thực Tế

Đặt Tên Branch Theo Tính Năng, Không Phải Ngày Tháng

Dùng tên branch mô tả như add-payment-table hay drop-legacy-columns thay vì migration-2024-07. Sáu tháng sau, bạn sẽ tự cảm ơn bản thân khi xem lại lịch sử deploy request.

Giữ Branch Ngắn Hạn

Development branch không tự đồng bộ các thay đổi schema từ main. Nếu branch của bạn tồn tại hàng tuần trong khi main nhận thêm các migration khác, bạn sẽ gặp merge conflict trong deploy request. Cố gắng mở, review và deploy trong vòng 1–3 ngày.

Pattern “Expand/Contract” Khi Đổi Tên Cột

PlanetScale không cho phép bạn đổi tên cột trong một deploy request nếu điều đó phá vỡ các query hiện có. Thay vào đó, dùng pattern expand/contract:

  1. Deploy: thêm cột mới user_name bên cạnh cột cũ username
  2. Cập nhật ứng dụng để ghi vào cả hai cột
  3. Backfill: sao chép dữ liệu từ cột cũ sang cột mới
  4. Deploy: cập nhật app để chỉ đọc từ cột mới
  5. Deploy: xóa cột cũ

Nhiều bước hơn, nhưng mỗi bước đều an toàn và có thể hoàn tác.

Dùng Boost Cho Cold Connection

Bản chất serverless của PlanetScale có nghĩa là cold start có thể gây ra đột biến latency khi kết nối. Bật PlanetScale Boost (query caching) cho các workload đọc nhiều, và dùng connection pooling ở tầng ứng dụng (không cần PgBouncer tương đương ở đây — PlanetScale xử lý điều này nội bộ).

Foreign Key Không Được Hỗ Trợ

Đây là điểm cần lưu ý lớn nhất. Vì PlanetScale được xây dựng trên Vitess (hỗ trợ horizontal sharding), các ràng buộc foreign key bị vô hiệu hóa. Thay vào đó, bạn phải đảm bảo tính toàn vẹn tham chiếu ở tầng ứng dụng. Nếu schema của bạn phụ thuộc nhiều vào foreign key, hãy cân nhắc điều này vào quyết định có chuyển sang PlanetScale không.

Khi Nào Nên Dùng PlanetScale

Sau khi dùng qua vài dự án, đây là đánh giá thành thật của tôi về khi nào nên chọn PlanetScale:

  • Phù hợp: Team thường xuyên triển khai thay đổi schema, ứng dụng mà downtime trong lúc migration là không chấp nhận được, startup muốn MySQL được quản lý mà không tốn công vận hành
  • Cân nhắc kỹ: Workload cần ràng buộc foreign key, ứng dụng phân tích nặng với nhiều JOIN (hãy cân nhắc data warehouse), hoặc team đã quen với gh-ost trên MySQL tự quản lý

Mô hình branching thực sự thay đổi cách bạn nghĩ về thay đổi database. Thay vì “hãy lên kế hoạch maintenance window,” cuộc trò chuyện trở thành “mở branch, thực hiện thay đổi, đưa đi review.” Sự thay đổi trong quy trình làm việc đó có giá trị hơn bất kỳ tính năng cụ thể nào.

Share: