
SQLITE WAL MODE: Tăng tốc độ ghi và đọc đồng thời
SQLite WAL (Write-Ahead Logging) là chế độ ghi nhớ trước dùng thay thế rollback journal truyền thống. Khi bật WAL, mọi thay đổi được ghi vào một file WAL riêng biệt thay vì ghi trực tiếp vào file database chính. Điều này cho phép đọc và ghi chạy đồng thời — độc giả không block người ghi và ngược lại. Đây là tính năng giúp SQLite vượt xa hầu hết các hệ quản trình khác trong các tình huống đọc-ghi đồng thời.
WAL có từ phiên bản SQLite 3.7.0 (2010-07-21) và ngày nay là chế độ mặc định trong nhiều framework và công cụ vì độ ưu việt về hiệu năng. Bài viết tham khảo từ SQLite WAL documentation và PRAGMA statements reference.
WAL hoạt động thế nào?
Đối với rollback journal truyền thống, SQLite ghi một bản sao của nội dung gốc vào file rollback journal, sau đó mới ghi thay đổi vào file database. Nếu rollback xảy ra, nội dung gốc được khôi phục. Quá trình này yêu cầu ghi hai lần — một lần vào journal và một lần vào database — làm giảm hiệu năng ghi.
Với WAL, cách tiếp cận đảo ngược: nội dung gốc được giữ nguyên trong file database, và các thay đổi được appended vào file WAL riêng. Commit xảy ra khi một bản ghi đặc biệt cho biết commit được ghi vào WAL — điều này có nghĩa là commit có thể thực hiện mà không cần ghi vào file database gốc, cho phép các độc giả tiếp tục đọc từ database gốc chưa bị thay đổi trong khi thay đổi đang được ghi vào WAL. Nhiều transaction có thể được append vào cuối cùng một file WAL duy nhất.

Checkpointing: chuyển WAL về database
Một checkpoint là thao tác chuyển nội dung từ file WAL trở lại file database. Có năm loại checkpoint:
- PASSIVE — checkpoint càng nhiều frames có thể mà không chờ đợi bất kỳ reader hay writer nào. Đây là mặc định khi gọi
wal_checkpointkhông có đối số. - FULL — blocks writers nhưng cho phép readers chạy. Đảm bảo tất cả frames trong WAL đều được chuyển.
- RESTART — giống FULL nhưng sau khi checkpoint xong, đảm bảo WAL bắt đầu lại từ đầu cho lần ghi kế tiếp.
- TRUNCATE — giống RESTART nhưng cắt ngắn file WAL về 0 byte sau khi hoàn thành.
- NOOP — không checkpoint bất kỳ frame nào, chỉ trả về thông tin thống kê.
Checkpoint tự động chạy khi WAL đạt 1000 trang (có thể thay đổi qua PRAGMA wal_autocheckpoint). Một checkpoint có thể chạy song song với readers, nhưng phải dừng nếu có reader đang đọc từ vùng cần ghi. Lập trình viên có thể tắt auto-checkpoint và chạy checkpoint trong thread riêng để tránh block thread chính.
PRAGMA WAL quan trọng
Bật WAL mode:
PRAGMA journal_mode = WAL;
Đặt ngưỡng tự động checkpoint (mặc định 1000 trang):
PRAGMA wal_autocheckpoint = 500;
Chạy checkpoint thủ công:
PRAGMA wal_checkpoint(FULL);
Lưu ý hiệu năng
Ghi nhanh hơn vì chỉ ghi một lần (thay vì hai lần như rollback journal), đồng thời I/O tuần tự hơn. Tuy nhiên, đọc sẽ chậm hơn khi file WAL lớn. Để duy trì hiệu năng đọc tốt, cần chạy checkpoint thường xuyên để giữ WAL nhỏ.
Với PRAGMA synchronous = NORMAL, checkpoint là thao tác duy nhất thực hiện fsync — nếu chạy trong thread riêng, thread chính sẽ không bao giờ block trên sync. Tuy nhiên, transactions sẽ không còn durable sau power failure. Nếu bạn cần durability, giữ synchronous = FULL.
Hạn chế của WAL
- Không hoạt động trên filesystem mạng — tất cả process phải trên cùng một máy vì WAL sử dụng shared memory (-shm file).
- Không thể thay đổi
page_sizesau khi bật WAL, kể cả với VACUUM hay restore. - File WAL và
shmphụ trợ tăng kích thước ứng dụng — không phù hợp khi dùng SQLite làm application file format. - Đối với ứng dụng đọc nhiều, ghi ít, WAL có thể chậm hơn 1-2% so với rollback journal.

Bảng so sánh journal_mode
| Journal mode | Đặc điểm | Hiệu năng |
|---|---|---|
| DELETE | Mặc định, xóa rollback journal sau mỗi transaction | Chuẩn, an toàn |
| TRUNCATE | Truncate journal về 0 byte thay vì delete | Nhanh hơn DELETE trên một số filesystem |
| PERSIST | Không xóa journal, chỉ ghi đầu với zeros | Ít syscall hơn |
| MEMORY | Lưu journal trong RAM | Rất nhanh nhưng không an toàn |
| WAL | Write-ahead log, đọc+ghi đồng thời | Nhanh nhất với workload đọc+ghi đồng thời |
| OFF | Tắt journal hoàn toàn | Nhanh nhất nhưng rủng rỗng dữ liệu |
Kết luận
WAL mode là lựa chọn tối ưu cho các ứng dụng có nhu cầu đọc-ghi đồng thời hoặc ghi nhiều. Kết hợp với PRAGMA synchronous = NORMAL và checkpoint trong thread riêng, bạn có thể đạt hiệu năng tối đa trong khi vẫn duy trì độ an toàn cần thiết.
Để bắt đầu, chạy PRAGMA journal_mode = WAL; và theo dõi dung lượng file -wal. Nếu đọc quá chậm, giảm wal_autocheckpoint. Nếu ghi quá chậm, thử synchronous = NORMAL. WAL là cải tiến lớn nhất của SQLite từ phiên bản 3.7.0, và là bắt buộc cho mọi ứng dụng production dùng SQLite với concurrent access.
