1. Transaction là gì?

Transaction (giao dịch) là một nhóm thao tác trên database được xem như một đơn vị xử lý logic. Mục tiêu là bảo đảm những thay đổi liên quan được thực hiện theo cách nhất quán: nếu giao dịch thành công thì các thay đổi cần thiết được xác nhận; nếu gặp lỗi khiến giao dịch không thể hoàn thành, các thay đổi chưa commit sẽ được rollback.

Transaction đặc biệt quan trọng khi một nghiệp vụ cần thay đổi nhiều bản ghi hoặc nhiều bảng nhưng không được phép để dữ liệu chỉ cập nhật một phần.

Ví dụ, khi chuyển 500.000 đồng từ tài khoản A sang tài khoản B, hệ thống cần thực hiện hai thao tác: trừ tiền của A và cộng tiền cho B. Nếu chỉ trừ tiền A thành công nhưng thao tác cộng tiền B thất bại, dữ liệu sẽ không còn phản ánh đúng giao dịch.

2. Khi nào cần sử dụng Transaction?

Không phải mọi câu SQL đều cần được nhóm vào một transaction tường minh. Nếu chỉ lấy danh sách sản phẩm bằng một câu SELECT, application thường không cần tự mở transaction nhiều câu lệnh.

Transaction trở nên quan trọng khi một nghiệp vụ gồm nhiều thao tác mà kết quả của chúng phải nhất quán với nhau.

Ví dụ, khi tạo đơn hàng, application có thể phải tạo bản ghi trong orders, thêm nhiều bản ghi order_items, giảm số lượng tồn kho và ghi nhận trạng thái liên quan. Nếu orders đã được tạo nhưng một thao tác quan trọng phía sau thất bại, hệ thống có thể lưu lại một đơn hàng chưa hoàn chỉnh.

Đưa các thao tác database liên quan vào cùng một transaction giúp application commit toàn bộ phần thay đổi cần thiết hoặc rollback khi phát hiện lỗi.

3. BEGIN, COMMIT và ROLLBACK

Ba thao tác thường gặp nhất khi làm việc với transaction là BEGIN, COMMIT và ROLLBACK.

BEGIN bắt đầu một transaction tường minh. Các câu lệnh SQL tiếp theo thuộc về transaction đó cho đến khi transaction kết thúc.

COMMIT xác nhận transaction. Nếu transaction thành công và commit được database chấp nhận, những thay đổi của nó được ghi nhận như một giao dịch hoàn tất.

ROLLBACK hủy các thay đổi chưa commit trong transaction hiện tại. Nó thường được dùng khi một thao tác thất bại hoặc khi application phát hiện điều kiện nghiệp vụ không được đáp ứng.

Ví dụ:

BEGIN;

UPDATE accounts
SET balance = balance - 500000
WHERE id = 1;

UPDATE accounts
SET balance = balance + 500000
WHERE id = 2;

COMMIT;

Đây là ví dụ minh họa cho một giao dịch chuyển tiền. Nếu cần hủy giao dịch trước khi commit, có thể thực hiện:

ROLLBACK;

4. ACID là gì?

ACID là bốn thuộc tính mô tả những bảo đảm quan trọng của transaction: Atomicity, Consistency, Isolation và Durability.

Bốn thuộc tính này giải quyết bốn vấn đề khác nhau. Atomicity bảo đảm giao dịch không bị hoàn thành một phần; Consistency liên quan đến việc duy trì các quy tắc hợp lệ của dữ liệu; Isolation kiểm soát tương tác giữa các transaction đồng thời; Durability bảo đảm tính bền vững của những thay đổi đã commit theo các bảo đảm mà database và cấu hình vận hành cung cấp.

Atomicity – Tính nguyên tử

Atomicity có thể hiểu là tất cả hoặc không gì cả đối với những thay đổi thuộc transaction.

Quay lại ví dụ chuyển tiền, giao dịch gồm hai thay đổi:

  • Trừ 500.000 đồng khỏi tài khoản A.
  • Cộng 500.000 đồng vào tài khoản B.

Nếu thao tác đầu thành công nhưng thao tác thứ hai thất bại, transaction cần được rollback để tránh tình trạng tiền bị trừ khỏi A nhưng không được cộng vào B.

Atomicity giúp application không phải tự xây dựng cơ chế hoàn tác thủ công cho từng câu lệnh trong một giao dịch database thông thường.

Consistency – Tính nhất quán

Consistency có nghĩa là transaction phải đưa database từ một trạng thái hợp lệ sang trạng thái hợp lệ khác theo những quy tắc và ràng buộc đã xác định.

Ví dụ, nếu số lượng sản phẩm trong kho không được âm, hệ thống phải bảo vệ quy tắc đó khi tạo đơn hàng. Nếu mỗi email phải duy nhất, database cần có unique constraint tương ứng để ngăn những bản ghi vi phạm quy tắc.

Cần hiểu rằng database không tự biết toàn bộ quy tắc nghiệp vụ của application. Consistency được bảo vệ bởi sự kết hợp giữa database constraints, transaction và logic nghiệp vụ được triển khai đúng cách.

Isolation – Tính cô lập

Isolation liên quan đến cách các transaction tương tác với nhau khi nhiều user hoặc request thao tác database cùng lúc.

Giả sử kho hàng chỉ còn một sản phẩm. Hai request cùng lúc kiểm tra tồn kho và đều nhận được kết quả là còn một sản phẩm. Nếu cả hai tiếp tục tạo đơn hàng mà không có cơ chế kiểm soát thích hợp, hệ thống có thể bán vượt số lượng thực tế.

Đây là vấn đề concurrency. Mỗi transaction có thể riêng lẻ trông hợp lý, nhưng khi nhiều transaction chạy đồng thời, kết quả kết hợp của chúng có thể vi phạm quy tắc nghiệp vụ.

Durability – Tính bền vững

Durability nghĩa là những thay đổi đã commit thành công được giữ lại theo các bảo đảm lưu trữ và phục hồi mà database cung cấp, kể cả khi xảy ra sự cố như tiến trình bị dừng hoặc server gặp crash trong những tình huống được hỗ trợ.

Ví dụ, khi hệ thống xác nhận một giao dịch ngân hàng đã thành công, application cần tin rằng giao dịch đó sẽ không biến mất chỉ vì database khởi động lại sau đó.

5. Locking và Race Condition trong Database

Transaction không tự động loại bỏ mọi race condition. Nếu hai request cùng đọc một giá trị rồi cập nhật dựa trên kết quả đó, dữ liệu vẫn có thể sai nếu application không sử dụng cơ chế đồng bộ phù hợp.

Ví dụ, số lượng tồn kho ban đầu là 1. Hai request cùng đọc thấy còn một sản phẩm, rồi mỗi request đều quyết định đặt mua. Nếu chúng không phối hợp đúng cách, cả hai có thể cố hoàn tất đơn hàng trong khi thực tế chỉ có đủ hàng cho một đơn.

Có hai cách tiếp cận thường gặp để xử lý loại tình huống này.

Cập nhật có điều kiện

Thay vì chỉ đọc số lượng rồi cập nhật sau, application có thể dùng một câu lệnh cập nhật có điều kiện:

UPDATE products
SET stock = stock - 1
WHERE id = 10
  AND stock >= 1;

Sau đó, kiểm tra số dòng đã được cập nhật. Nếu đúng một row bị thay đổi, việc trừ tồn kho thành công. Nếu không có row nào được cập nhật, sản phẩm có thể không tồn tại hoặc không còn đủ hàng, và application phải xử lý tình huống đó phù hợp.

Row Locking

Trong những nghiệp vụ cần đọc một bản ghi rồi quyết định thay đổi dựa trên dữ liệu đó, có thể dùng locking read như SELECT ... FOR UPDATE trong các hệ quản trị hỗ trợ phù hợp.

Row lock giúp các transaction cạnh tranh trên cùng bản ghi phải phối hợp theo cơ chế khóa, thay vì tự do sửa cùng dữ liệu cùng lúc. Tuy nhiên, locking cần được sử dụng cẩn thận vì có thể gây lock wait hoặc deadlock.

6. Deadlock và Retry Transaction

Deadlock xảy ra khi những transaction giữ các lock khác nhau và chờ nhau theo cách không thể tiếp tục. Ví dụ, transaction A giữ khóa của tài khoản 1 rồi chờ khóa tài khoản 2, trong khi transaction B giữ khóa tài khoản 2 rồi chờ tài khoản 1.

Database thường có cơ chế phát hiện deadlock và chọn một transaction làm nạn nhân để rollback, qua đó cho những transaction còn lại tiếp tục.

Một cách hạn chế deadlock là luôn cập nhật các tài nguyên theo một thứ tự nhất quán nếu nghiệp vụ cho phép. Ví dụ, khi phải khóa nhiều tài khoản, có thể chọn thứ tự theo ID thay vì mỗi request tự khóa theo thứ tự ngẫu nhiên.

Tuy nhiên, tránh deadlock hoàn toàn không phải lúc nào cũng khả thi. Khi cần retry, phải xác định loại lỗi có thể retry và giới hạn số lần thử. Không được tự động retry mọi lỗi mà không phân biệt, bởi lỗi validation hoặc thiếu tiền không thể giải quyết bằng cách thử lại ngay lập tức.

7. Tổng kết

Transactions & ACID là nền tảng để bảo đảm những thay đổi dữ liệu liên quan được xử lý đúng cách trong database.

Transaction gom nhiều câu lệnh thành một đơn vị logic. BEGIN bắt đầu giao dịch, COMMIT xác nhận những thay đổi và ROLLBACK hủy các thay đổi chưa commit khi giao dịch không thể hoàn thành.

ACID gồm bốn thuộc tính:

  • Atomicity: Các thay đổi thuộc transaction được xử lý theo nguyên tắc tất cả hoặc không gì cả.
  • Consistency: Những quy tắc và ràng buộc của dữ liệu cần được duy trì qua transaction.
  • Isolation: Các transaction đồng thời tương tác theo mức cô lập và locking được database hỗ trợ.
  • Durability: Những thay đổi đã commit được bảo vệ theo cơ chế lưu trữ và phục hồi của hệ thống.

Trong PostgreSQL và MySQL, cách transaction hoạt động chi tiết có thể khác nhau, đặc biệt ở isolation level mặc định và hành vi locking. Vì vậy, khi viết backend với Go hoặc Gin, cần hiểu cả transaction lẫn cách database cụ thể xử lý concurrency.

Bài viết khác

Series 3 – JavaScript Fundamentals: Async → API

Series 3 — JavaScript Fundamentals   JavaScript → DOM → Async → API   Bài 2. Asynchronous JavaScript & API – Xử lý bất đồng bộ và giao tiếp với Server 1. Vì sao cần Asynchronous JavaScript? Trong các website hiện đại, nhiều thao tác cần thời gian để hoàn thành, chẳng hạn như gọi API lấy […]

Database Normalization – 1NF, 2NF, 3NF & Denormalization

1. Database Normalization là gì? Database Normalization (chuẩn hóa dữ liệu) là quá trình tổ chức dữ liệu thành các bảng và quan hệ hợp lý nhằm giảm dư thừa không cần thiết, tránh dữ liệu mâu thuẫn và duy trì tính toàn vẹn của database. Khi thiết kế database, nếu lưu tất cả thông […]

Database Design & Relationships

1. Database Design là gì? Database Design là quá trình thiết kế cách tổ chức, lưu trữ và liên kết dữ liệu trong một hệ thống sao cho dữ liệu có cấu trúc hợp lý, hạn chế trùng lặp không cần thiết, bảo đảm tính toàn vẹn và hỗ trợ các truy vấn mà application […]

PostgreSQL / MySQL

1. PostgreSQL và MySQL là gì? PostgreSQL và MySQL là hai hệ quản trị cơ sở dữ liệu quan hệ (Relational Database Management System – RDBMS), được sử dụng để lưu trữ, truy vấn và quản lý dữ liệu cho các ứng dụng. Tuy cùng thuộc nhóm relational database, PostgreSQL và MySQL có sự khác […]

Sanity Testing(Kiểm thử sự hợp lý)

Sanity Testing là gì? Trong quá trình phát triển và vận hành một dự án phần mềm, hoạt động lập trình không bao giờ dừng lại ở lần phát hành đầu tiên. Sau khi nhận được phản hồi từ đội ngũ QC hoặc người dùng thực tế, các lập trình viên sẽ liên tục tiến […]

Laravel Routing & Controllers

Laravel Routing và Controllers là gì? 1. Laravel Routing là gì? Laravel Routing (định tuyến trong Laravel) là cơ chế xác định cách ứng dụng tiếp nhận và xử lý HTTP request dựa trên những thông tin như HTTP method và URL. Một route có thể chuyển request đến Controller, thực thi Closure (hàm ẩn […]

Leave a Reply

Your email address will not be published. Required fields are marked *