“SQLite chỉ để học” — một lầm tưởng phổ biến. Thực tế, SQLite chạy trong trình duyệt bạn đang đọc, trong Android/iOS của hàng tỷ thiết bị, trong phần mềm bay của máy bay Boeing, trong Chromium… và ngày càng nhiều team dùng nó cho backend ở quy mô vừa.
Bài này không cổ xuý “thay PostgreSQL bằng SQLite”. Mục tiêu: giúp bạn hiểu SQLite phù hợp khi nào, điều cần bật, các giới hạn thực sự và khi nào nên rời đi.
1. SQLite không phải server — đó là một thư viện
Đặc điểm gốc:
- Một file duy nhất chứa toàn bộ DB (header + page).
- Không có process DB riêng; code app link trực tiếp thư viện
sqlite3. - Truy cập đồng thời qua cùng file: lock ở mức file/OS.
- Không chạy qua mạng mặc định — vị trí vật lý quyết định ai truy cập được.
Hậu quả kiến trúc:
- Latency mỗi query cực thấp (không network, không IPC).
- Scale horizontal kiểu “nhiều máy client” không có theo nghĩa truyền thống.
- Backup/replication phải dùng công cụ ngoài (không có replication sẵn có).
2. Khi nào SQLite là lựa chọn tốt
2.1. Ứng dụng embedded và client-side
- Mobile app (Room, Core Data dùng SQLite dưới nền).
- Desktop app: Chrome, Firefox, Slack (cache), Signal.
- Game lưu save state, config.
2.2. Workload đọc nhiều, ghi ít trên 1 máy
- Read replica cho dashboard internal.
- Static site builder lưu index tìm kiếm.
- Cache nội bộ có SQL đầy đủ.
2.3. Service nhỏ, triển khai đơn giản
- Blog, forum quy mô nhỏ/trung bình.
- Admin tools, internal apps.
- CLI tool có persistent state.
- Microservice với state không quá lớn (< vài trăm GB).
Một số framework/platform cổ suý pattern này: Litestream cho backup streaming, LiteFS cho replication đọc, rqlite/dqlite cho HA Raft-based.
2.4. Edge computing
Chạy ở node edge gần user, mỗi node có bản đọc local, ghi qua leader — giảm latency cực mạnh cho đọc. Fly.io, Turso đẩy mạnh mô hình này.
2.5. Phân tích dữ liệu (analytical)
SQLite xử lý file vài GB SQL nhanh đáng ngạc nhiên. Để phân tích thực sự lớn, đã có DuckDB (cùng triết lý “embedded”) vượt trội về OLAP.
3. Khi nào không dùng SQLite
- Nhiều tiến trình ghi đồng thời trên nhiều máy qua network: SQLite chỉ khoá ở mức file một máy.
- Ghi QPS cao (> vài nghìn/s ổn định trên cùng DB): chỉ một writer active tại một thời điểm.
- Nhiều client từ xa cần quyền riêng, account, auth phức tạp: không có phía server xử lý.
- Replication sẵn có, failover tự động, multi-AZ: phải tự xây hoặc dùng wrapper (rqlite/dqlite).
- Dung lượng lớn hơn đĩa một máy: dù SQLite hỗ trợ DB tới 281 TB trên lý thuyết, thực tế phụ thuộc IO một host.
4. Bật các pragma quan trọng
Mặc định của SQLite thiên về tính đúng và tương thích, nhưng cho production bạn nên điều chỉnh.
4.1. WAL (Write-Ahead Logging)
PRAGMA journal_mode = WAL;
- Thay journal rollback mặc định bằng WAL.
- Cho phép đọc đồng thời khi đang ghi: reader không chặn writer, writer không chặn reader.
- Vẫn chỉ một writer tại một thời điểm.
- Sinh thêm file
-walvà-shm— khi backup phải copy cả ba.
4.2. Synchronous
PRAGMA synchronous = NORMAL;
FULL(mặc định): chắc chắn fsync sau mỗi commit — chậm nhưng cực kỳ bền.NORMAL: an toàn với WAL trong hầu hết kịch bản; nhanh hơn. Phù hợp production có UPS/file system ổn.OFF: không dùng ở production — mất điện có thể làm DB hỏng.
4.3. Khác đáng bật
PRAGMA busy_timeout = 5000; -- ms; chờ khi bị SQLITE_BUSY
PRAGMA foreign_keys = ON; -- ép kiểm tra FK (mặc định OFF vì lịch sử!)
PRAGMA temp_store = MEMORY; -- bảng tạm ở RAM
PRAGMA cache_size = -64000; -- ~64MB cache trang (âm = KB)
PRAGMA mmap_size = 268435456; -- 256MB mmap cho đọc nhanh
foreign_keys = ON phải bật mỗi connection (không bền trong file). Viết helper chạy ngay sau khi mở kết nối.
4.4. Kích thước page
PRAGMA page_size = 4096; -- hoặc 8192 tuỳ workload
VACUUM; -- áp dụng page_size mới
Thay đổi phải trước VACUUM/tạo bảng. Với workload nhiều cột text dài, page lớn hơn (8K/16K) có thể giảm overhead.
5. Concurrency: những gì thực sự xảy ra
5.1. Trong WAL
- Nhiều reader song song — mỗi transaction thấy snapshot tại thời điểm BEGIN.
- Một writer tại một thời điểm; writer khác chờ hoặc bị
SQLITE_BUSY.
5.2. SQLITE_BUSY và retry
Khi writer trùng nhau, bạn sẽ gặp lỗi này. Cách xử lý:
import sqlite3, time, random
def execute_with_retry(conn, sql, params=(), max_retry=5):
for i in range(max_retry):
try:
return conn.execute(sql, params)
except sqlite3.OperationalError as e:
if "database is locked" in str(e):
time.sleep(0.05 * (2 ** i) + random.random() * 0.01)
continue
raise
raise RuntimeError("busy after retries")
Hoặc đặt busy_timeout cao (SQLite tự retry trong core).
5.3. Transaction và write throughput
- Gom nhiều insert vào một transaction thay vì autocommit từng dòng. Khác biệt có thể 100–1000 lần throughput.
- Dùng prepared statement (
?placeholder) để tránh parse SQL nhiều lần. - Với bulk import, dùng
PRAGMA journal_mode=OFFtạm thời + transaction lớn (đảm bảo có thể retry cả từ đầu nếu lỗi).
5.4. Connection pool
Trong app multi-thread/async, dùng một connection pool nhỏ; SQLite thread-safe ở mode serialized nhưng vẫn chỉ có một writer. Một số ORM (như SQLAlchemy) có cấu hình riêng cho SQLite — đọc kỹ.
6. Backup và recovery
6.1. Không copy file đang chạy bằng cp
File có thể ở giữa một transaction → copy ra bản không nhất quán. Hai cách an toàn:
Dùng API backup chính thức (atomic, trong quá trình chạy):
sqlite3 app.db ".backup '/backup/app-$(date +%F).db'"
Dùng VACUUM INTO (SQLite 3.27+):
VACUUM INTO '/backup/app-2026-04-11.db';
Cả hai an toàn với app đang ghi.
6.2. Backup streaming với Litestream
Litestream chạy như sidecar, đọc WAL liên tục, đẩy lên S3/B2/GCS. Phục hồi là copy file gần nhất + replay WAL. Point-in-time recovery thực tế cho SQLite — một bước ngoặt đáng kể.
6.3. Kiểm tra bản backup
Thường xuyên thử restore ở môi trường staging. Backup chưa bao giờ được thử = không có backup.
7. Replication và HA
SQLite không có replication built-in. Ba hướng phổ biến:
7.1. Litestream replica + single writer
- Một instance ghi; Litestream stream WAL ra object storage.
- Khi fail, instance khác boot lên, restore từ replica, tiếp tục.
- RPO vài giây; RTO phụ thuộc tốc độ restore.
7.2. LiteFS
- Distributed file system trên SQLite của Fly.io; một leader ghi, nhiều follower đọc.
- Follower có bản gần real-time → đọc nhanh ở edge.
- Ghi vẫn phải qua leader — latency tuỳ vị trí.
7.3. rqlite / dqlite
- Wrapper Raft quanh SQLite → HA đa node, consensus.
- Mất tính “một file đơn giản” và hiệu năng write thấp hơn PostgreSQL tương đương. Hợp khi cần cluster đơn giản, workload nhẹ.
8. Bảo mật thực dụng
- File permission:
chowncho user app,chmod 600— không cho đọc tuỳ tiện. - Mã hoá: SQLite bản thường không encrypt at rest. Dùng SQLCipher hoặc mã hoá ở tầng filesystem (LUKS, eCryptfs).
- Backup cũng phải mã hoá nếu có PII (server-side encryption của S3 là tối thiểu).
- Migration tool: dùng công cụ như Alembic, Flyway, Goose — bản thân SQLite có
ALTER TABLEgiới hạn (không đổi type cột trực tiếp trước 3.35; vẫn phải làm cẩn thận).
9. Monitoring và troubleshooting
Kích thước file (
ls -lh app.db*): WAL lớn bất thường → checkpoint không chạy (writer giữ lâu).PRAGMA wal_checkpoint(TRUNCATE)ép checkpoint khi cần.Slow query: bật
sqlite3_trace_v2hoặc log ở app..timer ontrong CLI để benchmark.Integrity check định kỳ:
PRAGMA integrity_check;Fragmentation:
VACUUMđịnh kỳ (cẩn thận: lock DB khi chạy). HoặcPRAGMA auto_vacuum = INCREMENTAL+PRAGMA incremental_vacuum.
10. Ước lượng năng lực thực tế
Không có con số “chính thức” mà phụ thuộc hardware và schema, nhưng để tham khảo (nguồn: benchmark cộng đồng, post của Ben Johnson về Litestream):
- Đọc: có thể đạt hàng chục nghìn QPS trên máy hiện đại nhờ mmap + page cache.
- Ghi: vài nghìn TPS khi gom batch; vài trăm TPS nếu commit từng giao dịch.
- Kích thước thực tế chạy ổn: hàng chục GB dễ dàng, trăm GB vẫn được với tuning, TB thì nên cân nhắc lại.
Nếu bạn chưa đụng các giới hạn trên, SQLite là lựa chọn rất nghiêm túc.
11. Tóm tắt
- SQLite là thư viện, không phải server: triển khai cực gọn, zero-admin, latency thấp.
- Bật WAL +
synchronous=NORMAL+busy_timeout+foreign_keys=ONcho production. - Một writer tại một thời điểm — thiết kế app để ghi từ một entry point.
- Backup bằng
.backup/VACUUM INTO/ Litestream — đừngcp. - Replication không có sẵn; dùng Litestream / LiteFS / rqlite tuỳ yêu cầu HA.
- Không phù hợp khi cần nhiều writer cross-host, quy mô dữ liệu/QPS rất lớn, hoặc HA đa vùng phức tạp.
Với đúng workload, SQLite cho bạn một dịch vụ ít bộ phận chuyển động: ít container, ít config, ít thứ để hỏng. Đó là giá trị cực lớn cho team nhỏ và các hệ thống edge.