Thảm họa lúc 2 giờ sáng
Đó là lúc 2 giờ sáng, chiếc điện thoại trên tủ đầu giường bắt đầu rung bần bật khiến tôi tỉnh giấc. Bảng điều khiển giám sát (monitoring dashboard) của một công cụ SaaS nhỏ mà tôi quản lý hiện lên một màu đỏ rực. Ổ cứng SSD trên chiếc VPS giá 5 USD/tháng của tôi đã gặp lỗi phần cứng nghiêm trọng. Vì tôi đang chạy một thiết lập SQLite tiêu chuẩn, dữ liệu của tôi bị kẹt cứng trong một ổ đĩa đã ‘chết’ tại một trung tâm dữ liệu cách đó hàng trăm dặm. Suy nghĩ đầu tiên của tôi là sự hối hận tột độ: ‘Lẽ ra mình nên bỏ ra 15 USD mỗi tháng để thuê Postgres được quản lý (managed Postgres)’.
Tôi đã từng dùng PostgreSQL cho các cấu trúc quan hệ phức tạp và MongoDB để xây dựng nguyên mẫu nhanh chóng. Chúng là những tiêu chuẩn của ngành công nghiệp vì những lý do chính đáng. Nhưng đối với nhiều dự án cá nhân và ứng dụng quy mô vừa, chúng thực sự là ‘dùng dao mổ trâu giết gà’. Chúng gây ra độ trễ mạng, yêu cầu connection pooling và ngốn hàng trăm megabyte RAM. Tôi muốn tốc độ nguyên bản của SQLite — nơi các truy vấn thường chạy dưới 0,1 mili giây — mà không phải lo lắng về rủi ro ‘điểm lỗi duy nhất’ (single point of failure). Đó là lúc Litestream thay đổi mọi thứ.
Vấn đề: Tại sao các kỹ sư sợ dùng SQLite
SQLite cực kỳ nhanh vì nó nằm ngay bên trong tiến trình ứng dụng của bạn. Không có chi phí TCP hay việc phải đi tìm máy chủ cơ sở dữ liệu. Tuy nhiên, theo truyền thống, nó thất bại trong hai bài kiểm tra thực tế (litmus tests) cho môi trường production:
- Khôi phục sau thảm họa (Disaster Recovery): Nếu ổ NVMe của máy chủ bị hỏng hoặc nhà cung cấp phá sản, cơ sở dữ liệu của bạn sẽ biến mất.
- Khôi phục tại một thời điểm (Point-in-time Recovery): Việc chạy một cron job để sao chép file 2GB mỗi giờ là một giải pháp vụng về. Bạn có nguy cơ mất 59 phút dữ liệu người dùng nếu sự cố xảy ra ngay trước lần sao lưu tiếp theo.
Litestream giải quyết vấn đề này bằng cách hoạt động như một tiến trình ‘sidecar’. Nó giám sát Write-Ahead Log (WAL) của SQLite và truyền (stream) mọi giao dịch đến các kho lưu trữ đám mây như AWS S3 hoặc Cloudflare R2 gần như theo thời gian thực.
Cách thức hoạt động: Phép màu của chế độ WAL
Để sử dụng Litestream, bạn phải hiểu về Write-Ahead Logging (WAL). Ở chế độ tiêu chuẩn, SQLite ghi trực tiếp vào file cơ sở dữ liệu chính. Ở chế độ WAL, SQLite sẽ thêm các thay đổi vào một file phụ -wal trước. Điều này cho phép nhiều người đọc và một người ghi hoạt động đồng thời mà không chặn lẫn nhau.
Litestream theo dõi file WAL này cực kỳ sát sao. Mỗi khi một giao dịch được commit, Litestream sẽ ghi lại phần thay đổi (delta) và gửi nó đến bucket S3 của bạn. Nếu máy chủ của bạn gặp sự cố nghiêm trọng, bạn chỉ cần khởi tạo một instance mới. Sau đó, bạn yêu cầu Litestream khôi phục dữ liệu. Nó sẽ tải bản snapshot mới nhất và chạy lại các thay đổi tăng dần. Bạn hầu như không mất một chút dữ liệu nào.
Thực hành: Thiết lập SQLite + Litestream
Hãy cùng xây dựng một thiết lập có khả năng phục hồi cao. Chúng ta sẽ sử dụng Cloudflare R2 làm đích đến vì nó không mất phí truyền dữ liệu (egress fees) và có gói miễn phí 10GB rộng rãi — hoàn hảo cho các ứng dụng vừa và nhỏ.
1. Cài đặt Litestream
Litestream là một file thực thi Go duy nhất. Nó nhẹ và không có phụ thuộc. Trên máy chủ Linux, việc cài đặt chỉ mất vài giây:
curl -LO https://github.com/benbjohnson/litestream/releases/download/v0.3.13/litestream-v0.3.13-linux-amd64.tar.gz
tar -xzf litestream-v0.3.13-linux-amd64.tar.gz
sudo install litestream /usr/local/bin/litestream
2. Bật chế độ WAL
Ứng dụng của bạn cần yêu cầu SQLite sử dụng nhật ký WAL. Bạn có thể thực hiện việc này trong code (ví dụ: sử dụng câu lệnh PRAGMA) hoặc thủ công qua CLI:
sqlite3 /var/lib/my-app/data.db "PRAGMA journal_mode=WAL;"
3. Cấu hình sao lưu (Replication)
Tạo một file cấu hình tại /etc/litestream.yml. File này đóng vai trò là cầu nối giữa ổ đĩa cục bộ và đám mây.
access-key-id: <your-r2-key>
secret-access-key: <your-r2-secret>
dbs:
- path: /var/lib/my-app/data.db
replicas:
- url: s3://my-bucket-name.r2.cloudflarestorage.com/backup
4. Tự động hóa với Systemd
Bạn sẽ không muốn chạy lệnh này một cách thủ công. Hãy sử dụng một dịch vụ systemd để đảm bảo việc sao lưu bắt đầu ngay khi máy chủ khởi động. Tạo file /etc/systemd/system/litestream.service:
[Unit]
Description=Sao lưu Litestream
After=network.target
[Service]
ExecStart=/usr/local/bin/litestream replicate
Restart=always
[Install]
WantedBy=multi-user.target
Kích hoạt dịch vụ bằng lệnh: sudo systemctl enable --now litestream.
Giây phút quyết định: Kiểm tra khả năng khôi phục
Một bản sao lưu chỉ là một lời cầu nguyện cho đến khi bạn thực sự kiểm tra việc khôi phục. Hãy thử giả lập việc xóa sạch máy chủ. Xóa file cơ sở dữ liệu của bạn (hãy cẩn thận!):
rm /var/lib/my-app/data.db
Bây giờ, hãy hồi sinh nó:
litestream restore -o /var/lib/my-app/data.db s3://my-bucket-name.r2.cloudflarestorage.com/backup
Chỉ trong vài giây, cơ sở dữ liệu của bạn đã quay trở lại. Đối với quy trình sản xuất, bạn nên thêm lệnh khôi phục này vào entrypoint của Docker hoặc pipeline CI/CD. Điều này đảm bảo ứng dụng của bạn luôn khởi đầu với trạng thái mới nhất.
Sự đánh đổi: Giải pháp này có phù hợp với bạn?
Thiết lập này là “vùng Goldilocks” (vừa vặn nhất) cho nhiều dự án, nhưng nó không phải là “viên đạn bạc” cho mọi trường hợp.
Ưu điểm:
- Chi phí: Managed Postgres trên AWS RDS bắt đầu khoảng 15–30 USD/tháng. Thiết lập này tốn 0 USD với gói miễn phí của R2.
- Độ trễ bằng không: Ứng dụng của bạn giao tiếp với một file trên ổ NVMe cục bộ. Không có bước nhảy qua mạng (network hop) tới máy chủ cơ sở dữ liệu.
- Sự đơn giản: Không còn phải quản lý connection pools hay cấu hình VPC peering phức tạp.
Nhược điểm:
- Chỉ một người ghi (Single Writer): SQLite xử lý các truy vấn đọc đồng thời rất tốt, nhưng tại một thời điểm chỉ có một tiến trình có thể ghi. Nếu bạn có một ứng dụng lưu lượng cao với hơn 500 lượt ghi mỗi giây, hãy tiếp tục dùng Postgres.
- Chỉ mở rộng theo chiều dọc: Giải pháp này dành cho các ứng dụng chạy trên một node duy nhất. Nếu bạn cần mở rộng theo chiều ngang ra 10 máy chủ ứng dụng, bạn sẽ cần một DB phân tán như CockroachDB hoặc LiteFS.
Lời kết
Đừng mặc định chọn một cơ sở dữ liệu nặng nề và đắt đỏ chỉ vì bạn sợ lỗi phần cứng. SQLite kết hợp với Litestream mang lại cho bạn khả năng phục hồi của một hệ thống phân tán với sự đơn giản của một file cục bộ. Nó đã biến những cơn ác mộng lúc 2 giờ sáng của tôi thành những giấc ngủ ngon. Tôi biết rằng ngay cả khi nhà cung cấp VPS của tôi ngừng hoạt động vào ngày mai, dữ liệu của tôi vẫn an toàn trong một bucket được mã hóa, sẵn sàng để khôi phục trong vài giây. Hãy giữ cho stack của bạn tinh gọn, thực hiện sao lưu thời gian thực và chỉ thêm sự phức tạp khi lưu lượng truy cập thực sự yêu cầu.

