Menu

Savepoint trong SQLite: giao dịch lồng nhau với ROLLBACK TO

Cách savepoint hoạt động trong SQLite: các mốc có tên bên trong một giao dịch mà bạn có thể quay lại mà không phải vứt bỏ toàn bộ.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Savepoint là một dấu trang có tên bên trong giao dịch

Một giao dịch (transaction) thông thường là tất cả hoặc không gì cả: mọi thứ giữa BEGIN và COMMIT hoặc cùng được ghi, hoặc cùng bị vứt bỏ. Đó thường là điều bạn muốn, nhưng đôi khi bạn cần mức chi tiết hơn: "Thử loạt thay đổi này; nếu hỏng, chỉ hoàn tác loạt đó và giữ phần còn lại của giao dịch."

Savepoint chính là như vậy. Bạn đặt một dấu trang có tên, làm một số việc, rồi hoặc giữ lại kết quả (RELEASE) hoặc tua về dấu trang (ROLLBACK TO).

Khoản trừ của Ada và khoản cộng của Boris đều được giữ. Lệnh update nhầm vào Nobody đã được hoàn tác mà không mất phần còn lại của giao dịch.

Ba lệnh

Toàn bộ API chỉ có ba câu lệnh:

  • SAVEPOINT name: đặt một dấu trang.
  • RELEASE SAVEPOINT name: giữ lại phần việc đã làm từ dấu trang; bỏ dấu trang đi.
  • ROLLBACK TO SAVEPOINT name: hoàn tác mọi thứ từ dấu trang; dấu trang vẫn ở nguyên chỗ để bạn thử lại.

Từ SAVEPOINT sau RELEASE và ROLLBACK TO là tùy chọn: RELEASE risky và ROLLBACK TO risky đều chạy được.

Với cơ sở dữ liệu cuối cùng, hàng step 2 attempt coi như chưa từng tồn tại. Mọi thứ khác đều được ghi.

Savepoint không có giao dịch bao ngoài

Có một điểm nhỏ thú vị: bạn có thể gọi SAVEPOINT mà không cần BEGIN trước. SQLite lặng lẽ mở một giao dịch cho bạn, và savepoint ngoài cùng đóng vai trò chính giao dịch đó. RELEASE trên savepoint đó sẽ commit, còn ROLLBACK TO tua lại mà không commit.

Đó là lý do savepoint đôi khi được mô tả là "giao dịch có tên". Nhưng trộn hai kiểu này trong code thật sẽ gây rối, nên hãy chọn một. Hầu hết mọi người dùng BEGIN ... COMMIT rõ ràng cho ranh giới ngoài và chỉ dùng savepoint cho các điểm hoàn tác một phần bên trong.

Savepoint lồng nhau

Savepoint xếp chồng lên nhau. Bạn có thể đặt một savepoint bên trong savepoint khác và hoàn tác cái bên trong mà không động tới cái bên ngoài:

Nội dung cuối cùng: a, b, d. Quay lại inner đã xóa c nhưng giữ nguyên phần việc làm trước inner (lệnh insert b), và giao dịch vẫn tiếp tục.

Quay lại một savepoint bên ngoài cũng vứt bỏ mọi thứ đã làm ở các cấp bên trong: toàn bộ ngăn xếp phía trên tên đó được gỡ ra cùng lúc:

Cả b và c đều mất. ROLLBACK TO outer tua lại mọi thứ kể từ khi outer được đặt, bao gồm cả inner và lệnh insert c.

Vì sao dùng savepoint?

Trường hợp kinh điển là xử lý một lô dữ liệu, trong đó từng mục được phép thất bại mà không phải bỏ cả lô. Bọc mỗi mục trong một savepoint; nếu thất bại, quay lại savepoint và đi tiếp:

Trong code ứng dụng thật, lệnh insert lỗi sẽ ném ra lỗi, ứng dụng bắt lỗi đó, gọi ROLLBACK TO và tiếp tục. Hai hàng tốt được ghi; hàng lỗi không làm hỏng cả lô.

Mẫu này cũng là cách ORM và công cụ migration hiện thực giao dịch lồng nhau: chúng không thật sự lồng các khối BEGIN (SQLite không cho phép điều đó), mà ánh xạ các lời gọi lồng nhau thành savepoint.

Vài điều cần lưu ý

Một số chi tiết hay làm người mới dùng savepoint vấp:

  • COMMIT luôn commit toàn bộ giao dịch. Dù bạn có bao nhiêu savepoint đang mở, COMMIT (hoặc bí danh END của nó) đều đóng toàn bộ giao dịch ngoài cùng. Đừng coi RELEASE là commit một phần; không có gì được ghi bền vững cho đến khi giao dịch bao ngoài commit.
  • ROLLBACK (không có TO) hủy bỏ tất cả. Nó kết thúc giao dịch và vứt bỏ mọi savepoint đang mở. Dùng ROLLBACK TO name khi bạn muốn giữ giao dịch tiếp tục.
  • Savepoint vẫn mở cho đến khi được release hoặc bị rollback qua. Quên RELEASE không làm mất dữ liệu, nhưng dấu trang cứ nằm đó cho đến khi giao dịch kết thúc.
  • Tên không bắt buộc phải duy nhất. Nếu bạn đặt SAVEPOINT s hai lần, ROLLBACK TO s sẽ tìm cái gần nhất. Hữu ích cho đệ quy, nhưng gây rối nếu vô tình.

Tiếp theo: view

Savepoint cho bạn kiểm soát chi tiết hơn với thao tác ghi. Bước tiếp theo là định hình cách bạn đọc: lưu một truy vấn thành một đối tượng có tên, dùng lại được, mà bạn có thể SELECT như một bảng. Đó là view, và nó là nội dung tiếp theo.

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

Savepoint trong SQLite là gì?

Savepoint là một mốc có tên mà bạn đặt bên trong một giao dịch. Sau đó bạn có thể ROLLBACK TO tên đó để hoàn tác mọi thứ đã làm sau nó, hoặc RELEASE nó để giữ thay đổi và bỏ mốc đi. Savepoint cho phép bạn coi từng phần của giao dịch là những đơn vị nhỏ hơn, có thể khôi phục.

Savepoint khác giao dịch trong SQLite thế nào?

Một giao dịch bắt đầu bằng BEGIN và kết thúc bằng COMMIT hoặc ROLLBACK. Savepoint được đặt bên trong một giao dịch bằng SAVEPOINT name và cho bạn một điểm hoàn tác một phần. Quay lại một savepoint không kết thúc giao dịch bao ngoài; bạn có thể làm tiếp và commit sau.

Savepoint trong SQLite có lồng nhau được không?

Có. Bạn có thể xếp chồng các savepoint với tên khác nhau, và ROLLBACK TO outer hoàn tác mọi thứ về đến cấp đó, kể cả các savepoint bên trong. Tên không bắt buộc phải duy nhất: SQLite dùng savepoint gần nhất có tên đó, đúng theo hành vi ngăn xếp tiêu chuẩn.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu