SQLite trong production: khi nào đủ, khi nào phải rời đi

Chia sẻ bài viết

Mục lục
SQLite trong production: khi nào đủ, khi nào phải rời đi

Trả lời nhanh: SQLite chạy production nghiêm túc được cho app một máy: không server riêng, không cấu hình, độ trễ đọc gần bằng không vì dữ liệu nằm ngay trong tiến trình. Điều kiện bắt buộc: bật WAL mode (đọc không chặn ghi) + busy_timeout, và backup bằng lệnh .backup/VACUUM INTO thay vì copy file sống. Ranh giới thật sự: một máy và ghi không quá dồn dập, cần nhiều app server cùng ghi hoặc hàng trăm ghi đồng thời liên tục thì đến giờ chuyển Postgres.

"SQLite chỉ để học" là định kiến hết hạn từ lâu: nó đang chạy trong mọi điện thoại, trình duyệt, và ngày càng nhiều SaaS một máy ăn nên làm ra. Với dân vibe code và indie hacker, SQLite + VPS là combo khởi đầu gọn không gì bằng. Bài này: cấu hình đúng để nó bền, hiểu giới hạn thật thay vì lời đồn, và lộ trình rời đi êm ái khi lớn.

Tóm tắt nhanh
  • SQLite = database trong file, chạy trong tiến trình app, không server, không port, không mật khẩu để lộ
  • WAL mode là công tắc quan trọng nhất: đọc song song thoải mái trong lúc ghi
  • Giới hạn thật: một máy ghi tại một thời điểm, nhiều app server cùng ghi là hết vai
  • Backup phải bằng .backup/VACUUM INTO, copy file đang sống có thể ra bản hỏng

Vì sao SQLite đáng được tôn trọng

Mỗi query Postgres/MySQL là một chuyến khứ hồi mạng (dù là localhost); mỗi query SQLite là một lời gọi hàm trong chính tiến trình app, độ trễ đọc thấp đến mức khái niệm N+1 query bớt đáng sợ hẳn. Không dịch vụ để quản, không port để lộ, backup là một file. Ngưỡng chịu tải thực tế cao hơn lời đồn xa: site đọc-là-chính hàng trăm nghìn lượt/ngày trên một VPS là chuyện bình thường của SQLite có WAL. Chính n8n bạn cài hôm trước cũng khởi đầu bằng SQLite.

Bộ cấu hình bắt buộc cho production

PRAGMA journal_mode = WAL;      -- doc khong chan ghi, ghi khong chan doc
PRAGMA busy_timeout = 5000;     -- cho khoa 5s thay vi loi ngay SQLITE_BUSY
PRAGMA synchronous = NORMAL;    -- can bang ben-nhanh chuan cho WAL
PRAGMA foreign_keys = ON;       -- rang buoc khoa ngoai (mac dinh tat!)

WAL (Write-Ahead Log) là thay đổi lớn nhất: chế độ mặc định cũ khóa cả file khi ghi, nguồn gốc mọi tiếng xấu; WAL cho phép nhiều người đọc trong lúc một người ghi. ORM hiện đại (Drizzle, Prisma) đều cho đặt PRAGMA lúc mở kết nối, cho vào code khởi tạo, đừng trông chờ mặc định.

Giới hạn thật - và giới hạn đồn

  • Thật: một máy. File SQLite trên NFS/mount mạng chia cho nhiều app server là công thức hỏng dữ liệu kinh điển. SQLite = kiến trúc một máy, chấm.
  • Thật: một người ghi tại một thời điểm. WAL cho ghi tuần tự rất nhanh (hàng chục nghìn giao dịch nhỏ/giây trên NVMe), nhưng trăm worker cùng chen ghi liên tục sẽ xếp hàng, app kiểu đó thuộc về Postgres.
  • Đồn: dữ liệu lớn là chết. File vài chục GB vẫn chạy tốt nếu có index tử tế, giới hạn lý thuyết còn xa lắm.
  • Đồn: không an toàn. ACID đầy đủ, kiểm thử thuộc hàng khắt khe nhất giới phần mềm. Mất dữ liệu SQLite gần như luôn do backup sai cách (mục dưới) chứ không do engine.

Backup đúng và lộ trình lớn lên

# SAI: cp app.db backup/   ← file dang song, co the dinh trang thai do dang
# DUNG:
sqlite3 app.db ".backup /var/backups/app-$(date +%F).db"
# hoac gon hon tren ban moi: VACUUM INTO '/var/backups/app.db'

Cho vào cron hằng đêm, giữ 7 bản, cộng backup máy chủ định kỳ là hai lớp đủ ngủ ngon.

Dấu hiệu đến giờ chuyển Postgres: cần app server thứ hai; log xuất hiện SQLITE_BUSY dù đã busy_timeout (ghi tranh nhau thật sự); cần tính năng ngoài phạm vi (full-text nặng, pgvector). Đường chuyển êm: schema SQLite gọn thường dịch thẳng sang Postgres, ORM đổi connection string, dữ liệu bơm qua một script, chuẩn bị trước bằng thói quen SQL chuẩn, tránh đặc sản riêng.

Cấu hình bắt buộc cho bản chạy thật

-- Bat che do ghi nhat ky truoc, cho phep doc va ghi dong thoi
PRAGMA journal_mode = WAL;

-- Can bang giua an toan va toc do
PRAGMA synchronous = NORMAL;

-- Cho phep cho khi bang dang bi khoa, thay vi bao loi ngay
PRAGMA busy_timeout = 5000;

-- Bat rang buoc khoa ngoai, mac dinh SQLite TAT
PRAGMA foreign_keys = ON;

-- Kiem lai
PRAGMA journal_mode; PRAGMA synchronous; PRAGMA foreign_keys;

Dòng đầu và dòng cuối là hai dòng thay đổi cục diện. Không bật chế độ ghi nhật ký trước thì mọi lệnh ghi khóa toàn bộ tệp, và ứng dụng báo lỗi cơ sở dữ liệu đang bận ngay khi có hai người dùng cùng lúc. Không bật ràng buộc khóa ngoại thì dữ liệu mồ côi tích tụ âm thầm.

Giới hạn thật và giới hạn đồn

Điều hay ngheThực tế
SQLite chỉ dùng để họcChạy tốt cho ứng dụng đọc nhiều, ghi vừa phải, một máy chủ
Không chịu được nhiều người dùngĐọc đồng thời rất tốt; giới hạn nằm ở ghi, vì mỗi lúc chỉ một lệnh ghi
Không chịu được dữ liệu lớnKích thước tệp không phải rào cản thực tế với ứng dụng vừa và nhỏ
Chạy được trên nhiều máy chủ ứng dụngKhông, đây là giới hạn thật: tệp nằm trên một máy
Không cần sao lưu vì chỉ là một tệpNgược lại, một tệp hỏng là mất tất cả

Sao lưu đúng cách

# SAI: chep tep khi ung dung dang chay, ban sao co the hong
cp app.db /sao-luu/

# DUNG: dung lenh sao luu cua chinh SQLite
sqlite3 app.db ".backup '/sao-luu/app-$(date +%F).db'"

# Kiem ban sao co doc duoc khong, chay ngay sau khi sao luu
sqlite3 /sao-luu/app-$(date +%F).db "PRAGMA integrity_check;"

# Don ban cu
find /sao-luu -name "app-*.db" -mtime +30 -delete

Khi nào phải rời đi

  • Cần chạy nhiều máy chủ ứng dụng cùng lúc. Đây là lý do cứng, không có cách vòng.
  • Lệnh ghi bắt đầu xếp hàng: ứng dụng báo cơ sở dữ liệu đang bận thường xuyên dù đã đặt thời gian chờ.
  • Cần nhiều người cùng ghi vào một bảng liên tục, ví dụ hệ thống đặt chỗ theo thời gian thực.
  • Chưa chạm vào ba điều trên thì đừng chuyển. Chuyển sớm là thêm một thành phần phải vận hành mà chưa đổi lại được gì.

Câu hỏi thường gặp

Web bán hàng nhỏ chạy SQLite được không?

Được và nhiều bên đang chạy: đọc nhiều ghi vừa (đơn hàng vài trăm/ngày là 'ghi vừa'), một VPS, backup đêm. Đến khi có nhiều máy hoặc ghi dồn dập mới cần nghĩ tiếp.

SQLite trên VPS đặt file ở đâu cho chuẩn?

Ổ cục bộ của VPS (không mount mạng), thư mục riêng của app, quyền chỉ user app đọc ghi. Trên ổ NVMe, fsync của WAL gần như miễn phí, trải nghiệm ghi mượt rõ so với ổ thường.

Litestream/LiteFS là gì, có nên dùng?

Công cụ stream WAL sang S3 để backup liên tục/replica đọc, nâng cấp đáng giá khi SQLite thành trái tim sản phẩm. Khởi đầu thì cron .backup là đủ.

n8n của tôi đang SQLite, có phải chuyển Postgres không?

Chưa cần nếu chạy êm. Chuyển khi execution log lớn làm giao diện ì hoặc workflow chạy dày, đúng như checklist trong bài cài n8n.

Hạ tầng NVMe Enterprise của TND
VPS ổ Enterprise U.2 NVMe, CPU xung cao, đặt tại Việt Nam
SQLite + một VPS là cấu hình khởi nghiệp gọn nhất 2026, và ổ đĩa chính là 'server' của SQLite: trên TND VPS NVMe, mỗi fsync WAL đi thẳng xuống Enterprise U.2 NVMe RAID 10, đọc nóng nằm cache, Xeon Platinum xung cao chạy query trong tiến trình lẹ. Kèm backup máy chủ hàng tuần làm lớp bảo hiểm thứ hai cho file dữ liệu của bạn. Bàn giao 5 phút.
Xem VPS ổ Enterprise U.2 NVMe tại Việt Nam

Bài viết liên quan