Trong bài này
Cô Mai thấy hai số dư đã thay đổi sau một lần chuyển xu và muốn biết thao tác có đáng tin hay không. Đúng hai con số cuối chưa trả lời được mọi câu hỏi. Cô còn cần biết chuyện gì xảy ra khi có lỗi, có người thao tác cùng lúc hoặc máy chủ dừng hoạt động.
Tính nguyên tử giữ các bước trong cùng một quyết định
ACID là tên ghép từ bốn thuộc tính của transaction: nguyên tử, nhất quán, cô lập và bền vững. Mỗi thuộc tính trả lời một nhóm rủi ro khác nhau. Không thuộc tính nào tự biết toàn bộ ý định của người viết chương trình.
Tính nguyên tử (atomicity) nghĩa là nhóm thay đổi được xác nhận trọn vẹn hoặc không giữ thay đổi nào của nhóm. Trong lần chuyển xu, câu trừ và câu cộng cần có chung quyết định. Giữ riêng một phía sẽ làm số xu tổng bị sai.
Ở bộ dữ liệu mẫu, ta thử chuyển 5 xu từ Chi (mã 3) sang Lan (mã 6), rồi quyết định hủy:
Dữ liệu đầu vào
students6 dòng| id | name | coins |
|---|---|---|
| 1 | An | 120 |
| 2 | Bình | 90 |
| 3 | Chi | 180 |
| 4 | Dung | 60 |
| 5 | Huy | 180 |
| 6 | Lan | 150 |
Câu lệnh
BEGIN;
UPDATE students
SET coins = coins - 5
WHERE id = 3;
UPDATE students
SET coins = coins + 5
WHERE id = 6;
ROLLBACK;
SELECT id, name, coins
FROM students
WHERE id IN (3, 6)
ORDER BY id;Bảng students sau khi chạy
students6 dòng| id | name | grade | city | coins | mentor |
|---|---|---|---|---|---|
| 1 | An | 6 | Hà Nội | 120 | Cô Mai |
| 2 | Bình | 7 | Đà Nẵng | 90 | NULL |
| 3 | Chi | 8 | Hà Nội | 180 | Thầy Nam |
| 4 | Dung | 6 | Huế | 60 | NULL |
| 5 | Huy | 9 | Cần Thơ | 180 | Cô Mai |
| 6 | Lan | 7 | Hà Nội | 150 | Thầy Nam |
| id | name | coins |
|---|---|---|
| 3 | Chi | 180 |
| 6 | Lan | 150 |
Chi vẫn có 180 xu và Lan vẫn có 150 xu. Hai phép sửa đã thuộc cùng phạm vi bị hủy, nên không phía nào giữ phần thay đổi. Ví dụ này kiểm hành vi hoàn tác, chưa mô phỏng việc mất điện giữa lúc ghi xuống đĩa.
Bạn phải chọn ranh giới đủ rộng cho công việc. Nếu ứng dụng cần lưu cả số dư và dòng lịch sử, chỉ nhóm hai số dư là chưa bao trọn yêu cầu. Phần lịch sử nằm ngoài có thể thất bại riêng dù hai số dư đã đúng.
Mở transactionXác định phạm vi cần chungGhi các bướcTrừ và cộng đúng ngườiKiểm điều kiệnĐối chiếu yêu cầu công việcChọn một kết thúcĐạt: xác nhận, lỗi: hủy
Lỗi SQL không luôn tự chọn nhánh hủy toàn bộ. Như bài 27 đã chỉ ra, ứng dụng còn phải bắt lỗi và kết thúc transaction phù hợp. Tính nguyên tử chỉ bảo vệ đúng nhóm thao tác mà bạn thực sự đặt trong cùng phạm vi.
Tính nhất quán cần quy tắc đúng và kiểm tra đủ
Tính nhất quán (consistency) đòi dữ liệu sau thao tác thỏa các quy tắc cần giữ. Bất biến (invariant) là một điều kiện phải đúng trước và sau thao tác hợp lệ. Với chuyển xu, một bất biến là tổng số xu không tăng hoặc giảm.
Ta đọc tổng của cả bảng trước khi chuyển. Hàm SUM cộng số dư của mọi học viên, kể cả khi nhiều người có cùng một số dư:
Dữ liệu đầu vào
students6 dòng| id | name | coins |
|---|---|---|
| 1 | An | 120 |
| 2 | Bình | 90 |
| 3 | Chi | 180 |
| 4 | Dung | 60 |
| 5 | Huy | 180 |
| 6 | Lan | 150 |
Câu lệnh
SELECT SUM(coins) AS total
FROM students;Kết quả
| total |
|---|
| 780 |
Kết quả có total bằng 780. Đây là giá trị đọc từ bộ mẫu, không phải hằng số được áp cho mọi bộ dữ liệu. Sau một lần chuyển hợp lệ, tổng cần bằng tổng trước, còn mọi số dư vẫn không âm.
Để kiểm thêm số dư thấp nhất, ta dùng hàm MIN trên cùng bảng nguồn:
Dữ liệu đầu vào
students6 dòng| id | name | coins |
|---|---|---|
| 1 | An | 120 |
| 2 | Bình | 90 |
| 3 | Chi | 180 |
| 4 | Dung | 60 |
| 5 | Huy | 180 |
| 6 | Lan | 150 |
Câu lệnh
SELECT MIN(coins) AS lowest
FROM students;Kết quả
| lowest |
|---|
| 60 |
Giá trị lowest bằng 60, nên bộ mẫu chưa có ai âm xu. Cần kiểm lại cả hai điều kiện sau khi chuyển. Bạn có thể kết hợp hai hàm trong một truy vấn ở phần thực hành.
Ràng buộc CHECK(coins >= 0) bảo vệ số dư từng học viên. Thử chuyển vượt khả năng của người gửi sẽ gặp lỗi ngay tại câu trừ:
Dữ liệu đầu vào
students6 dòng| id | name | coins |
|---|---|---|
| 1 | An | 120 |
| 2 | Bình | 90 |
| 3 | Chi | 180 |
| 4 | Dung | 60 |
| 5 | Huy | 180 |
| 6 | Lan | 150 |
Câu lệnh
UPDATE students
SET coins = coins - 121
WHERE id = 1;Thông báo lỗi
coins>=0
students6 dòng| id | name | grade | city | coins | mentor |
|---|---|---|---|---|---|
| 1 | An | 6 | Hà Nội | 120 | Cô Mai |
| 2 | Bình | 7 | Đà Nẵng | 90 | NULL |
| 3 | Chi | 8 | Hà Nội | 180 | Thầy Nam |
| 4 | Dung | 6 | Huế | 60 | NULL |
| 5 | Huy | 9 | Cần Thơ | 180 | Cô Mai |
| 6 | Lan | 7 | Hà Nội | 150 | Thầy Nam |
An vẫn giữ số dư cũ sau lỗi. Tuy nhiên, ràng buộc này không kiểm tổng của nhiều người. Bạn vẫn cần đối chiếu tổng trước và sau để phát hiện việc cộng hoặc trừ nhầm phía.
Một transaction vẫn có thể chạy thành công mà sai ý định. Đoạn sau cộng cho cả hai người thay vì chuyển giữa họ:
Dữ liệu đầu vào
students6 dòng| id | name | coins |
|---|---|---|
| 1 | An | 120 |
| 2 | Bình | 90 |
| 3 | Chi | 180 |
| 4 | Dung | 60 |
| 5 | Huy | 180 |
| 6 | Lan | 150 |
Câu lệnh
BEGIN;
UPDATE students
SET coins = coins + 10
WHERE id IN (1, 2);
COMMIT;
SELECT MIN(coins) AS lowest, SUM(coins) AS total
FROM students;Bảng students sau khi chạy
students2 dòng thay đổi| id | name | grade | city | coins | mentor |
|---|---|---|---|---|---|
| 1 | An | 6 | Hà Nội | 130 | Cô Mai |
| 2 | Bình | 7 | Đà Nẵng | 100 | NULL |
| 3 | Chi | 8 | Hà Nội | 180 | Thầy Nam |
| 4 | Dung | 6 | Huế | 60 | NULL |
| 5 | Huy | 9 | Cần Thơ | 180 | Cô Mai |
| 6 | Lan | 7 | Hà Nội | 150 | Thầy Nam |
| lowest | total |
|---|---|
| 60 | 800 |
Tổng tăng thành 800, dù mọi số dư vẫn không âm. Nhóm thao tác đã được xác nhận cùng nhau, nhưng quy tắc chuyển xu bị vi phạm. Chỉ có transaction không đủ để biến công thức sai thành một lần chuyển đúng.
| Điều kiện | Công cụ hỗ trợ | Điều còn phải kiểm |
|---|---|---|
| Không có số dư âm | CHECK từng dòng | Người gửi đủ xu cho thao tác |
| Tổng không đổi | So sánh SUM trước và sau | Không cộng hoặc trừ nhầm phía |
| Đủ lịch sử | Ghi cùng phạm vi cần thiết | Nội dung từng dòng lịch sử |
Kiểm một con số có thể bỏ sót lỗi khác. Tổng vẫn đúng nếu trừ nhầm một người và cộng cho đúng người nhận. Vì vậy, cần kiểm cả mã, lượng xu và phần không được phép thay đổi.
Tính cô lập liên quan đến nhiều thao tác cùng lúc
Tính cô lập (isolation) quy định cách các transaction tương tác khi chạy đồng thời. Mục đích là tránh việc chúng tùy tiện nhìn hoặc tác động lên các trạng thái trung gian của nhau. Bảo đảm cụ thể phụ thuộc hệ quản trị và mức cô lập đang dùng.
Một kết nối có thể đọc chính thay đổi chưa xác nhận của nó. Ví dụ sau chỉ quan sát từ kết nối đang thực hiện lần thử:
Dữ liệu đầu vào
students6 dòng| id | name | coins |
|---|---|---|
| 1 | An | 120 |
| 2 | Bình | 90 |
| 3 | Chi | 180 |
| 4 | Dung | 60 |
| 5 | Huy | 180 |
| 6 | Lan | 150 |
Câu lệnh
BEGIN;
UPDATE students
SET coins = coins - 10
WHERE id = 1;
SELECT id, coins
FROM students
WHERE id = 1;
ROLLBACK;Bảng students sau khi chạy
students6 dòng| id | name | grade | city | coins | mentor |
|---|---|---|---|---|---|
| 1 | An | 6 | Hà Nội | 120 | Cô Mai |
| 2 | Bình | 7 | Đà Nẵng | 90 | NULL |
| 3 | Chi | 8 | Hà Nội | 180 | Thầy Nam |
| 4 | Dung | 6 | Huế | 60 | NULL |
| 5 | Huy | 9 | Cần Thơ | 180 | Cô Mai |
| 6 | Lan | 7 | Hà Nội | 150 | Thầy Nam |
| id | coins |
|---|---|
| 1 | 110 |
Kết quả đọc ở giữa là 110, còn bảng sau hủy lại có 120 tại mã 1. Hai hình ảnh thuộc hai thời điểm trên cùng kết nối. Chúng không cho biết một kết nối khác sẽ nhìn thấy gì khi lần chuyển đang mở.
Để suy luận về cô lập, cần phân biệt ít nhất hai phiên thao tác. Hình sau chỉ nêu câu hỏi cần kiểm, không phải kết quả chạy đồng thời trong phòng học:
Phiên ABắt đầu thay đổi số dưPhiên AChưa xác nhậnPhiên BThử đọc hoặc sửa dữ liệuQuan sátPhiên B thấy gì, có phải chờ không
Một lịch chạy tuần tự trên một kết nối chỉ chứng minh kết quả của lịch ấy. Nó không thay cho thử nghiệm hai yêu cầu có thể xen kẽ. Khi một ứng dụng gặp xung đột, còn cần chính sách xử lý lỗi hoặc thử lại phù hợp.
Tính bền vững bắt đầu sau khi xác nhận thành công
Tính bền vững (durability) nói rằng thay đổi đã xác nhận được giữ theo bảo đảm của hệ quản trị và cấu hình lưu trữ. Nó khác việc nhìn thấy kết quả trên màn hình hiện tại. Cần phân biệt dữ liệu đang ở bộ nhớ với dữ liệu đã được lưu bền vững.
Đoạn sau xác nhận một thay đổi rồi đọc lại trên cùng cơ sở dữ liệu thử:
Dữ liệu đầu vào
students6 dòng| id | name | coins |
|---|---|---|
| 1 | An | 120 |
| 2 | Bình | 90 |
| 3 | Chi | 180 |
| 4 | Dung | 60 |
| 5 | Huy | 180 |
| 6 | Lan | 150 |
Câu lệnh
BEGIN;
UPDATE students
SET coins = coins + 1
WHERE id = 2;
COMMIT;
SELECT id, name, coins
FROM students
WHERE id = 2;Bảng students sau khi chạy
students1 dòng thay đổi| id | name | grade | city | coins | mentor |
|---|---|---|---|---|---|
| 1 | An | 6 | Hà Nội | 120 | Cô Mai |
| 2 | Bình | 7 | Đà Nẵng | 91 | NULL |
| 3 | Chi | 8 | Hà Nội | 180 | Thầy Nam |
| 4 | Dung | 6 | Huế | 60 | NULL |
| 5 | Huy | 9 | Cần Thơ | 180 | Cô Mai |
| 6 | Lan | 7 | Hà Nội | 150 | Thầy Nam |
| id | name | coins |
|---|---|---|
| 2 | Bình | 91 |
Bình có 91 xu sau xác nhận. Điều này chứng minh câu cập nhật và lần đọc lại đã chạy trong lượt thử. Nó chưa chứng minh dữ liệu sống sót qua mất điện hoặc hỏng thiết bị lưu trữ.
| Câu hỏi | Cách kiểm phù hợp |
|---|---|
| Cập nhật đã được xác nhận trong lượt thử chưa? | Đóng transaction và đọc lại |
| Dữ liệu còn sau khi khởi động lại không? | Kiểm trên cơ sở dữ liệu lưu thành tệp với cấu hình phù hợp |
| Có phục hồi sau xóa nhầm đã xác nhận không? | Kiểm bản sao lưu và thử khôi phục |
Sao lưu giải quyết những rủi ro mà bền vững không tự ngăn được. Một người có quyền vẫn có thể xóa nhầm rồi xác nhận, khiến thao tác sai được lưu bền vững. Khi đó, cần lịch sử hoặc bản sao lưu phù hợp để phục hồi.
Phòng mẫu được tạo mới cho mỗi lượt chạy nhằm giúp bạn thử an toàn. Việc dữ liệu mẫu trở lại ở lượt kế tiếp là thiết kế của phòng học. Nó không phải bằng chứng rằng COMMIT trong hệ thống dùng tệp không giữ được dữ liệu.
Lỗi thường gặp
| Lỗi | Vì sao sai | Cách sửa |
|---|---|---|
| Có transaction là kết luận thuật toán đúng | Cộng nhầm hai phía vẫn có thể được xác nhận | Kiểm bất biến và đúng đối tượng |
| Chỉ kiểm tổng xu | Trừ nhầm người vẫn có thể giữ tổng | Đối chiếu từng dòng liên quan |
| Dùng một kết nối để kết luận về cô lập | Chưa quan sát tương tác nhiều phiên | Ghi rõ giới hạn của phép thử |
| Coi bền vững là thay cho sao lưu | Thao tác xóa nhầm đã xác nhận vẫn có thể được lưu | Chuẩn bị và thử khôi phục bản sao |
Tóm tắt
- Nguyên tử bảo vệ việc xác nhận hoặc hủy chung một nhóm thay đổi.
- Nhất quán cần các quy tắc được diễn tả đúng và được kiểm đủ.
- Cô lập mô tả tương tác nhiều transaction, không thể kết luận từ một lượt chạy tuần tự.
- Bền vững liên quan dữ liệu đã xác nhận và lưu trữ, còn sao lưu giúp phục hồi các sự cố khác.