Giao dịch và bảng ảo trong SQLite
(misfra.me)- Bảng ảo của SQLite cũng có thể hỗ trợ ghi và giao dịch, bằng cách triển khai các hook như
xUpdate,xSync,xCommit,xRollback - SQLite mặc định đảm bảo tính nguyên tử bằng cơ chế rollback journal, và khi xử lý nhiều tệp DB thì dùng super-journal để điều phối việc commit toàn cục
- Bảng ảo cũng là một phần của giao thức giao dịch của SQLite, nên nếu
xSyncthất bại thì toàn bộ giao dịch sẽ bị rollback - Commit được chia thành 2 giai đoạn;
xSynclà nơi thực hiện các tác vụ có thể thất bại, cònxCommitchỉ nên làm các tác vụ dọn dẹp đơn giản xCommitvàxRollbackluôn có thể được gọi, vì vậy cần viết chúng như các hàm dọn dẹp có thể chạy mà không thất bại
Bảng ảo và xử lý giao dịch trong SQLite
Trong bài viết trước, tác giả đã giới thiệu cách cơ bản để đăng ký và truy vấn bảng ảo của SQLite bằng ngôn ngữ Go. Bài này tập trung vào cách triển khai bảng ảo có thể ghi và hỗ trợ giao dịch.
Hỗ trợ ghi và giao dịch cho bảng ảo
-
Giao diện bảng ảo của SQLite không chỉ là chỉ đọc
-
Nếu triển khai hook
xUpdatethì có thể ghi ra cả nguồn dữ liệu bên ngoài -
Để có được tính nhất quán giao dịch thực sự, cần các hook giao dịch sau:
xBegin: thông báo bắt đầu giao dịchxSync: chuẩn bị để commit an toàn xuống đĩa (nếu thất bại ở đây thì rollback toàn bộ)xCommit: commit cuối cùng và dọn dẹpxRollback: thực hiện rollback nếu giao dịch bị hủy
-
Ngay cả khi được sửa đổi cùng với bảng thường hoặc bảng ảo khác, SQLite vẫn phối hợp tất cả các hook để đảm bảo tính nguyên tử
Cách giao dịch của SQLite hoạt động bên trong
Rollback journal
- SQLite mặc định lưu trang vào tệp sao lưu (journal) trước khi ghi đè
- Nếu có sự cố, nó sẽ phục hồi từ journal để đảm bảo tính nguyên tử
> Lưu ý: SQLite cũng hỗ trợ chế độ WAL, nhưng không nằm trong phạm vi bài viết này
Super-journal
-
Khi có nhiều cơ sở dữ liệu được gắn vào, chỉ journal riêng cho từng DB là khó đồng bộ
-
Một tệp cấp cao hơn gọi là super-journal được dùng để điều phối commit giữa nhiều tệp
-
Nếu chỉ xử lý nhiều bảng ảo trong cùng một tệp DB thì có thể đồng bộ mà không cần super-journal
-
Dù trong trường hợp nào, SQLite vẫn tự động gọi các hook
xSync,xCommit,xRollbacktrong luồng giao dịch
Commit hai giai đoạn cùng bảng ảo
Quá trình commit của SQLite gồm hai giai đoạn:
Giai đoạn 1: xSync (đảm bảo durability)
- Đồng bộ an toàn xuống đĩa mọi trang hoặc journal của mọi B-Tree và tệp DB
- Mỗi bảng ảo cũng sẽ được gọi hook
xSynctương ứng - Nếu bất kỳ
xSyncnào thất bại thì toàn bộ giao dịch sẽ bị rollback → giữ tính nguyên tử
Giai đoạn 2: dọn dẹp (xCommit)
-
Sau khi dữ liệu đã được ghi xong xuống đĩa, SQLite xóa tệp journal và thực hiện dọn dẹp cho bảng ảo
-
Dưới đây là một phần mã từ
vdbeaux.cdisable_simulated_io_errors(); sqlite3BeginBenignMalloc(); for(i=0; i<db->nDb; i++){ Btree *pBt = db->aDb[i].pBt; if( pBt ){ sqlite3BtreeCommitPhaseTwo(pBt, 1); } } sqlite3EndBenignMalloc(); enable_simulated_io_errors(); sqlite3VtabCommit(db); -
Bên trong
sqlite3VtabCommit(), trên thực tế mọi lời gọixCommitđều bị bỏ qua ngay cả khi thất bại → đây là giai đoạn dọn dẹp thuần túyint sqlite3VtabCommit(sqlite3 *db){ callFinaliser(db, offsetof(sqlite3_module,xCommit)); return SQLITE_OK; } -
Vì độ bền đã được đảm bảo ở
xSync, nên lỗi củaxCommithayxRollbackcũng sẽ bị bỏ qua
Những điểm cần lưu ý cho người viết bảng ảo
- Mọi tác vụ có tính bền vững phải được đặt trong
xSync- Các thao tác có thể thất bại như network I/O, ghi tệp... cần được xử lý ở đây để giao dịch có thể bị hủy an toàn
- Ngay cả sau
xSync,xRollbackvẫn có thể được gọi- Nếu
xSynccủa bảng khác thất bại thì toàn bộ giao dịch sẽ rollback
- Nếu
xCommitvàxRollbackphải được viết như các hàm dọn dẹp không được thất bại- Chúng nên idempotent (tính lũy đẳng), nghĩa là gọi nhiều lần cũng không làm thay đổi trạng thái
Kết luận
- Cơ chế journaling của SQLite đảm bảo commit nguyên tử cho mọi thành phần, bao gồm cả bảng thường và bảng ảo
- Các hook giao dịch của bảng ảo được tích hợp tự nhiên vào luồng giao dịch của SQLite
- Nhà phát triển triển khai bảng ảo nên tập trung vào
xSyncđể đảm bảo tính toàn vẹn dữ liệu, còn tác vụ dọn dẹp nên được tách sangxCommitvàxRollback
1 bình luận
Ý kiến trên Hacker News
vtabkhá hay. Tôi đã triển khai hỗ trợvtabkhi tái hiện thực SQLite bằng Rust. Vì vậy gần đây tôi đã học được rất nhiều điều vềvtab.vtabrất mạnh mẽ và có lẽ chưa được tận dụng đủ nhiềumattngo-sqlite3. Đây là CGO