CodeGym /Các khóa học /SQL SELF /Ví dụ sử dụng transaction trong các tình huống thực tế

Ví dụ sử dụng transaction trong các tình huống thực tế

SQL SELF
Mức độ , Bài học
Có sẵn

Đôi khi transaction giống như các nhân vật siêu anh hùng trong phim vậy. Chúng cứu database của chúng ta khỏi thảm họa khi có sự cố, lỗi hoặc trục trặc. Nếu bạn làm việc với một task cần thực hiện nhiều thao tác mà không thể tách rời, transaction sẽ đảm bảo mọi thứ được thực hiện cùng nhau. Cùng xem nó hoạt động thế nào qua ví dụ xử lý thanh toán nhé.

Xử lý thanh toán

Hãy tưởng tượng một tình huống kinh điển: bạn có hai tài khoản ngân hàng, và mình muốn chuyển tiền từ tài khoản này sang tài khoản kia. Đây không chỉ là thao tác "một nút bấm" đâu. Cần đảm bảo là mình đã trừ tiền đúng ở tài khoản gửi và cộng vào tài khoản nhận. Bất kỳ lỗi nào cũng có thể gây thảm họa: hoặc cả hai tài khoản không thay đổi gì, hoặc mất cân bằng (ví dụ tiền biến mất hoặc tự dưng xuất hiện "từ không khí").

Kịch bản: Chuyển tiền giữa các tài khoản

Đây là code của chúng ta. Đọc kỹ nhé, như thể đây là thông điệp từ một thiên hà xa xôi:

-- Bắt đầu transaction
BEGIN;

-- Bước 1. Trừ tiền từ tài khoản gửi
UPDATE accounts
SET balance = balance - 100
WHERE account_id = 1;

-- Bước 2. Cộng tiền vào tài khoản nhận
UPDATE accounts
SET balance = balance + 100
WHERE account_id = 2;

-- Mọi thứ ổn chứ? Vậy thì lưu thay đổi lại!
COMMIT;

Điều gì quan trọng ở đây?

  • Nếu ở Bước 1 hoặc Bước 2 có gì đó không ổn (ví dụ lỗi query, thiếu tiền), transaction có thể được quay lại bằng ROLLBACK, và dữ liệu sẽ trở về trạng thái ban đầu.
  • COMMIT đảm bảo rằng thay đổi chỉ được áp dụng nếu TẤT CẢ các bước đều thành công.

Thêm kiểm tra số dư

Giả sử tài khoản gửi không đủ tiền để chuyển thì sao? Thêm kiểm tra số dư để không "đẩy nó vào âm".

-- Bắt đầu transaction
BEGIN;

-- Lấy số dư hiện tại của tài khoản gửi
DO $$
DECLARE
    current_balance NUMERIC;
BEGIN
    SELECT balance INTO current_balance FROM accounts WHERE account_id = 1;

    -- Kiểm tra có đủ tiền không
    IF current_balance >= 100 THEN
        -- Nếu đủ tiền, thực hiện chuyển khoản
        UPDATE accounts
        SET balance = balance - 100
        WHERE account_id = 1;

        UPDATE accounts
        SET balance = balance + 100
        WHERE account_id = 2;

        -- Lưu thay đổi
        COMMIT;
    ELSE
        -- Nếu không đủ tiền, quay lại
        ROLLBACK;
        RAISE NOTICE 'Khong du tien de chuyen!';
    END IF;
END $$;

Điều gì thú vị hơn ở đây?

  • Mình dùng block PL/pgSQL với kiểm tra điều kiện bằng IF. Nếu số dư nhỏ hơn số tiền cần chuyển, transaction sẽ bị từ chối và không có gì thay đổi.
  • ROLLBACK hủy các thay đổi nếu có (dù ở đây chưa có gì để hủy, nhưng làm vậy là chuẩn bài).
Quan trọng!

Bài giảng này nói về các tình huống thực tế dùng transaction, nên mình lấy luôn ví dụ đời thường. Nó có stored procedure và viết bằng PL-SQL. Mình nghĩ các bạn đã đủ kinh nghiệm để hiểu cách nó hoạt động rồi. Sau này tụi mình sẽ quay lại chủ đề PL-SQL và còn xem nhiều ví dụ phức tạp hơn nữa.

Cập nhật hàng loạt dữ liệu trong transaction

Tạo transaction không chỉ hữu ích cho việc chuyển tiền đâu nhé. Giả sử bạn có database của shop online, mỗi ngày có hàng chục đơn hàng đổi trạng thái, ví dụ từ "dang giao" sang "hoan thanh". Làm sao để cập nhật nhiều bản ghi cùng lúc mà nếu có lỗi thì vẫn có thể quay lại? Tất nhiên là dùng transaction rồi.

Xem thêm một kịch bản nữa: cập nhật trạng thái đơn hàng.

Ví dụ nè:

-- Bắt đầu transaction
BEGIN;

-- Bước 1. Cập nhật đơn hàng có ngày giao đã qua
UPDATE orders
SET status = 'hoan thanh'
WHERE delivery_date < CURRENT_DATE;

-- Bước 2. Thông báo cập nhật thành công
RAISE NOTICE 'Tat ca trang thai don hang da duoc cap nhat thanh cong.';

-- Áp dụng thay đổi
COMMIT;

Nếu có gì đó không ổn thì sao?

Luôn có khả năng xảy ra lỗi. Ví dụ bạn quên mất điều kiện WHERE, và giờ tất cả đơn hàng đều đổi trạng thái thành hoan thanh. Để tránh tình huống này, cần kết thúc transaction hoặc chủ động quay lại nó.

Xem kịch bản rollback nhé:

-- Bắt đầu transaction
BEGIN;

-- Bước 1. Thử cập nhật đơn hàng mà không có điều kiện (ôi không, lỗi rồi!)
UPDATE orders
SET status = 'hoan thanh';

-- Quay lại transaction vì lỗi
ROLLBACK;

-- Giờ các đơn hàng vẫn không thay đổi

Thêm chút "linh hoạt" với SAVEPOINT

Không phải lúc nào cũng cần rollback toàn bộ transaction. Nếu kịch bản của bạn có nhiều bước, có thể bạn chỉ muốn rollback một phần thôi. Lúc này SAVEPOINT sẽ giúp bạn.

Kịch bản bây giờ là: xử lý nhiều bước với khả năng rollback từng bước.

Giả sử bạn xử lý đơn hàng gồm nhiều bước: trừ hàng trong kho, cập nhật trạng thái đơn, gửi thông báo cho khách. Nếu gửi thông báo bị lỗi, bạn chỉ muốn rollback bước đó thôi, còn các thay đổi khác vẫn giữ lại.

-- Bắt đầu transaction
BEGIN;

-- Bước 1. Trừ hàng trong kho
UPDATE products
SET stock = stock - 1
WHERE product_id = 101;

-- Lưu điểm rollback
SAVEPOINT step1;

-- Bước 2. Cập nhật trạng thái đơn hàng
UPDATE orders
SET status = 'shipped'
WHERE order_id = 202;

-- Thử gửi thông báo cho khách
SAVEPOINT step2;
-- Ôi, lỗi khi gửi thông báo!
ROLLBACK TO SAVEPOINT step2;

-- Quyết định kết thúc transaction an toàn
COMMIT;

Kết luận

Transaction không chỉ là công cụ kỹ thuật, nó là bảo đảm cho sự toàn vẹn dữ liệu của bạn. Nó bảo vệ khỏi "hiệu ứng domino", khi một lỗi có thể phá hỏng cả hệ thống. Mỗi lần bạn thực hiện nhiều thao tác liên quan, hãy tự hỏi: "Nếu một cái bị fail thì sao?" Nếu câu trả lời là "thảm họa", thì đã đến lúc dùng transaction rồi đó. Nhớ nhé: thà mất vài phút viết transaction còn hơn mất hàng giờ để khôi phục dữ liệu sau sự cố. Người dùng của bạn (và cả thần kinh của bạn) sẽ cảm ơn bạn nhiều lắm!

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION