Trong thế giới phát triển phần mềm ngày nay, dữ liệu đã trở thành một tài sản quý giá và việc quản lý nó đúng cách là cần thiết để đảm bảo hoạt động hiệu quả của các hệ thống và ứng dụng.
Một trong những yếu tố quan trọng nhất của việc quản lý dữ liệu là đảm bảo rằng nó được lưu trữ và cập nhật một cách đáng tin cậy, một việc mà các hệ thống quản trị cơ sở dữ liệu (DBMS) chịu trách nhiệm.
Để thực hiện điều này, hầu hết các DBMS tuân thủ theo một tập hợp các nguyên tắc được gọi là ACID, nhằm đảm bảo tính toàn vẹn và độ tin cậy của dữ liệu trong suốt quá trình giao dịch.
Trong bài blog này, tôi sẽ đi sâu vào phân tích các thuộc tính của ACID, thảo luận về tầm quan trọng của chúng và cách thức thường được triển khai trong hệ thống quản trị cơ sở dữ liệu. Tôi cũng sẽ thảo luận về lợi ích của việc duy trì các thuộc tính ACID và một số vấn đề tiềm ẩn có thể xảy ra nếu chúng không được duy trì.
1. Transaction
Vì ACID là thuộc tính của transaction nên chúng ta cần hiểu khái niệm transaction là gì trước. Một transaction đề cập đến một đơn vị logic của công việc bao gồm một hoặc nhiều hoạt động của cơ sở dữ liệu.
⇒ Như vậy, chúng ta có thể coi một transaction là một tập hợp các câu query trong cơ sở dữ liệu.
Sau đây là ví dụ của một transaction trong lĩnh vực tài chính:
Chuyển $100 từ Account 1 tới Account 2.
Transaction này bao gồm các hoạt động của cơ sở dữ liệu như sau:
- Tạo một record để chuyển 100 từ tài khoản A tới tài khoản B. Đây thường được coi là BEGIN của transaction.
- Đọc số dư của tài khoản 1.
- Nếu số dư của tài khoản 1 ≥ 100, trừ tài khoản 1 đi 100.
- Đọc số dư của tài khoản 2.
- Cộng 100 vào số dư của tài khoản 2.
- Nếu mọi thứ thành công, transaction sẽ được COMMIT để đánh dấu sự thay đổi trong cơ sở dữ liệu là vĩnh viễn. Nếu có lỗi xảy ra thì transaction sẽ được ROLLBACK, tức mọi thay đổi sẽ trở về trạng thái ban đầu.
2. ACID là gì?
Năm 1983, hai giáo sư Khoa học máy tính là Theo Härder (Theo Har-der) và Andreas Reuter (An-drei-as Rao-tờ) đã xuất bản một bài báo chuyên đề có tiêu đề Principles of transaction-oriented database recovery (Nguyên tắc phục hồi cơ sở dữ liệu hướng giao dịch) trên tạp chí Khảo sát điện toán ACM. Bài báo này phác thảo các nguyên tắc cơ bản đảm bảo xử lý giao dịch đáng tin cậy trong các hệ thống quản trị cơ sở dữ liệu, sau này được gọi là các thuộc tính ACID.
ACID là một bộ các thuộc tính của transaction trong hệ quản trị cơ sở dữ liệu (DBMS) nhằm đảm bảo tính hợp lệ của dữ liệu trong trường hợp có lỗi, mất điện và các rủi ro khác.
3. Tính chất của ACID
ACID bao gồm bộ 4 thuộc tính, viết tắt tương ứng với các chữ cái:
- A (Atomicity - Tính nguyên tử)
- C (Consistency - Tính nhất quán)
- I (Isolation - Tính cô lập)
- D (Durability - Tính bền vững)
3.1. Atomicity (A)
3.1.1. Atomicity là gì?
Atomicity (Tính nguyên tử) là chuỗi hoạt động của database không thể phân chia và không thể rút gọn, sao cho tất cả các bước đều xảy ra hoặc là không gì xảy ra.
Atomicity is an indivisible and irreducible series of database operations such that either all occurs, or nothing occurs.
Nguồn: Wikipedia
Nếu có một transaction bị lỗi thì transaction đó sẽ được ROLLBACK, dữ liệu sẽ không thay đổi, còn nếu không xảy ra lỗi thì transaction sẽ được COMMIT, dữ liệu trong cơ sở dữ liệu sẽ được cập nhật thành công.
Dân gian có một câu thành ngữ rất hay mô tả ngắn gọn tính nguyên tử là "Được ăn cả, ngã về không".
3.1.2. Ví dụ về Atomicity
Tôi có ví dụ sau: Chuyển 100 đô la từ Account 1 sang Account 2
- Trường hợp 1 tuân thủ tính nguyên tử: Transaction thành công, mọi thứ êm đẹp.
- Trường hợp 2 không tuân thủ tính nguyên tử: Số balance của Account 1 thay đổi nhưng số balance của Account 2 vẫn giữ nguyên.
3.1.3. Làm sao giúp hệ thống đảm bảo Atomicity?
Có nhiều cách để đảm bảo tính nguyên tử trong cơ sở dữ liệu, sau đây, tôi xin liệt kê một vài cách:
- Sử dụng hệ thống quản trị cơ sở dữ liệu (DBMS) tuân thủ ACID: DBMS tuân thủ ACID có các cơ chế tích hợp sẵn để đảm bảo tính nguyên tử. Đây thường là các đơn giản nhất vì DBMS sẽ tự động xử lý giúp bạn.
- Triển khai Two-Phase Commit (2PC): Đây là một giao thức được sử dụng trong các hệ thống phân tán để đảm bảo rằng một transaction chỉ được cam kết nếu tất cả các nút tham gia đồng ý. Điều này đảm bảo rằng một transaction chỉ được cam kết khi tất cả các nút có dữ liệu được cập nhật và đồng bộ hóa.
- Sử dụng transaction Logs: Cơ sở dữ liệu duy trì các transaction log ghi lại tất cả các hành động được thực hiện trong suốt giao dịch. Trong trường hợp xảy ra lỗi, nhật ký có thể được ROLLBACK để giúp hệ thống trở về trạng thái trước khi bắt đầu transaction.
3.2. Consistency (C)
3.2.1. Consistency là gì?
Consistency (Tính nhất quán) yêu cầu rằng tất cả transaction chỉ có thể thay đổi dữ liệu theo những cách được cho phép.
Consistency (or correctness) refers to the requirement that any given database transaction must change affected data only in allowed ways. Any data written to the database must be valid according to all defined rules, including constraints, cascades, triggers, and any combination thereof.
Nguồn: Wikipedia
Sau khi thực hiện một transaction, tất cả các ràng buộc của cơ sở dữ liệu phải được duy trì. Nếu một transaction vi phạm bất kỳ ràng buộc nào, nó phải bị huỷ bỏ và hệ thống phải quay trở lại trạng thái trước khi transaction diễn ra.
3.2.2. Ví dụ về Consistency
Tôi có ví dụ về không tuân thủ tính nhất quán do không tuân thủ ràng buộc về foregin key (khoá ngoài) giữa hai bảng custormers
và accounts
như sau.
- Bạn có 2 bảng
customers
vàaccounts
. Bảngcustomers
có khoá ngoàiacount_id
trỏ tớiid
của bảngaccounts
như hình. accounts
không có record vớiid
= 3 nhưng bảng customers lại có 1 record cóaccount_id
= 3.
3.2.3. Làm sao giúp hệ thống đảm bảo Consistency?
Có nhiều cách để đảm bảo tính nhất quán trong cơ sở dữ liệu, sau đây, tôi xin liệt kê một vài cách:
- Sử dụng hệ thống quản trị cơ sở dữ liệu (DBMS) tuân thủ ACID: DBMS tuân thủ ACID có các cơ chế tích hợp sẵn để đảm bảo tính nhất quán. Đây thường là các đơn giản nhất vì DBMS sẽ tự động xử lý giúp bạn.
- Kiểm tra tính hợp lệ của dữ liệu: Trước và sau khi thực hiện một transaction, kiểm tra tính hợp lệ của dữ liệu để đảm bảo rằng nó thỏa mãn tất cả các ràng buộc và quy tắc của hệ thống.
- Rollback và Recovery: Trong trường hợp transaction không thể được hoàn thành thành công và cần phải được hủy bỏ, hệ thống phải có cơ chế để ROLLBACK về trạng thái dữ liệu trước khi transaction bắt đầu.
- Sử dụng database versioning (quản lý phiên bản): Trong một số cơ sở dữ liệu, các phiên bản của một bản ghi có thể được lưu để đảm bảo consistency trong trường hợp có nhiều transaction cùng lúc.
- Đảm bảo tính cô lập và tính nguyên tử của transaction: Các cấp độ cô lập khác nhau có thể đảm bảo các transaction không thể can thiệp vào nhau, từ đó, làm giảm tính nhất quán của cơ sở dữ liệu. Điều này ngăn ngừa các vấn đề lỗi trong lúc đọc như dirty reads (đọc thay đổi chưa commit), repeatable reads (đọc thay đổi đã commit) và phantom reads (đọc thay đổi khi có record mới thêm vào cơ sở dữ liệu). Đừng lo nếu bạn không hiểu các lỗi đọc này là gì vì tôi giải thích kỹ ở phần Isolation phía sau.
- Triển khai Two-Phase Commit (2PC): Đây là một giao thức được sử dụng trong các hệ thống phân tán để đảm bảo rằng một transaction chỉ được cam kết nếu tất cả các nút tham gia đồng ý. Điều này đảm bảo rằng một transaction chỉ được cam kết khi tất cả các nút có dữ liệu được cập nhật và đồng bộ hóa.
Tùy thuộc vào yêu cầu và đặc điểm cụ thể của từng hệ thống, một hoặc nhiều cách tiếp cận này có thể được sử dụng để đảm bảo tính nhất quán.
3.3. Isolation (I)
3.3.1. Isolation là gì?
Isolation (Tính cô lập) đảm bảo các transaction được thực thi một cách độc lập, không phụ thuộc lẫn nhau.
Isolation: Events within a transaction must be hidden from other transactions running concurrently.
Trích Principles of transaction-oriented database recovery (Nguyên tắc phục hồi cơ sở dữ liệu hướng giao dịch)
Trong tính cô lập, chúng ta có hai khái niệm quan trọng là
- Read phenomena (Hiện tượng đọc)
- Isolation levels (Cấp độ cô lập)
3.3.2. Read phenomena (Hiện tượng đọc)
Read phenomena (Hiện tượng đọc) là tất cả những lỗi có thể xuất hiện khi nhiều người đọc và ghi vào cùng một dòng.
Chúng ta có 3 loại lỗi khi đọc phổ biến
- Dirty reads: lỗi khi đọc những thay đổi chưa được commit.
- Non - repeatable reads: lỗi khi đọc thay đổi đã được commit.
- Phantom reads: lỗi đọc khi thêm record mới vào bảng.
3.3.3. Isolation levels (Cấp độ cô lập)
Isolation levels (Cấp độ cô lập) chỉ các cấp độ cô lập trong một transaction nhằm khắc phục hiện tượng đọc gây lỗi (Read phenomena).
Chúng ta có 4 cấp độ cô lập xếp theo thứ tự mức cô lập cao nhất tới thấp nhất. Mức cô lập càng cao thì hiệu năng lại càng giảm.
- Serializable: Các transaction được thực hiện thực hiện tuần tự. Đây là cấp độ cô lập cao nhất và có tính concurency thấp nhất.
- Repeatable read: Mỗi query trong 1 transaction chỉ nhìn thấy các thay đổi đã được commit trước khi transaction đó bắt đầu.
- Read committed: Mỗi query trong 1 transaction chỉ nhìn thấy các thay đổi đã được commit trước khi query bắt đầu.
- Read uncommitted: Transaction có thể đọc những thay đổi chưa được commit hoặc đã được commit bởi những transaction khác. Đây là cấp độ cô lập thấp nhất và có tính concurency cao nhất.
Mối tương quan giữa hiện tượng đọc (read phenomenas) ở các cấp độ cô lập (isolation levels) mô tả như sau:
- Mức cô lập càng cao thì các lỗi đọc (read phenomena) càng ít xảy ra hơn.
- Ở mức độ cô lập cao nhất - Serializable, chúng ta không thấy xuất hiện một lỗi đọc nào. Ngược lại, ở mức độ cô lập thấp nhất - Read uncommitted, ta thấy xuất hiện tất cả các lỗi đọc.
Mức cô lập mặc định ở các hệ thống quản trị cơ sở dữ liệu:
⇒ Như vậy, ta có thể kết luận Postgres 15 chạy nhanh hơn MySQL 8 vì có mức độ cô lập thấp hơn.
3.3.4. Làm sao giúp hệ thống đảm bảo Isolation?
Có nhiều kỹ thuật để đảm bảo tính cô lập trong cơ sở dữ liệu, sau đây, tôi xin liệt kê một vài kỹ thuật:
- Sử dụng hệ thống quản trị cơ sở dữ liệu (DBMS) tuân thủ ACID: DBMS tuân thủ ACID có các cơ chế tích hợp sẵn để đảm bảo tính cô lập. Đây thường là các đơn giản nhất vì DBMS sẽ tự động xử lý giúp bạn.
- Triển khai Two-Phase Commit (2PC): Đây là một giao thức được sử dụng trong các hệ thống phân tán để đảm bảo rằng một transaction chỉ được cam kết nếu tất cả các nút tham gia đồng ý. Điều này đảm bảo rằng một transaction chỉ được cam kết khi tất cả các nút có dữ liệu được cập nhật và đồng bộ hóa.
Bạn cần lưu ý rằng tuỳ vào từng trường hợp cụ thể, chúng ta có thể áp dụng linh hoạt một hay nhiều kỹ thuật.
3.4. Durability (D)
3.4.1. Durability là gì?
Durability (Tính bền vững) đảm bảo rằng những transaction đã commit, kết quả của nó sẽ được lưu trữ vĩnh viễn và không thể thay đổi hoặc mất mát, ngay cả trong trường hợp có lỗi hệ thống, cúp điện hoặc các sự cố khác.
Durability: Once a transaction has been completed and has committed its results to the database, the system must guarantee that these results survive any subsequent malfunctions.
Trích: Principles of transaction-oriented database recovery (Nguyên tắc phục hồi cơ sở dữ liệu hướng giao dịch)
3.4.2. Ví dụ về Durability
Tôi có ví dụ sau:
Giả sử bạn đang thực hiện một giao dịch trực tuyến để chuyển 1000 đô la từ tài khoản ngân hàng của bạn sang tài khoản của một người thân. Đây coi như một transanction được thực hiện qua Internet và bao gồm nhiều bước như sau:
- BEGIN transaction
- Xác nhận số dư hiện tại
- Giảm 1000 đô la từ tài khoản của bạn
- Tăng 1000 đô la vào tài khoản của người thân
- Cập nhật các bản ghi tương ứng
- COMMIT transaction
Khi tất cả các bước này đã được hoàn thành và transaction đã được commit, tính bền vững đảm bảo rằng các thay đổi đó sẽ được lưu trữ vĩnh viễn trong cơ sở dữ liệu của ngân hàng.
Điều này có nghĩa là ngay cả nếu ngay sau đó xảy ra một lỗi hệ thống hoặc cúp điện, các thay đổi đã được thực hiện sẽ không bị mất và tài khoản của cả hai người sẽ vẫn hiển thị số dư mới đã được cập nhật.
3.4.3. Làm sao giúp hệ thống đảm bảo Durability?
Để đảm bảo tính bền vững trong một cơ sở dữ liệu, chúng ta có thể sử dụng một số cách tiếp cận và kỹ thuật sau:
- Sử dụng transaction logs: Một kỹ thuật phổ biến để đảm bảo tính bền vững là sử dụng log files. Các transaction được ghi vào một tệp log trước khi được cam kết vào cơ sở dữ liệu. Nếu hệ thống gặp lỗi, tệp log có thể được sử dụng để khôi phục lại cơ sở dữ liệu vào trạng thái hợp lệ cuối cùng.
- Sử dụng hệ thống lưu trữ dự phòng: Các cơ sở dữ liệu thường được sao lưu trên nhiều hệ thống lưu trữ hoặc nhiều vị trí địa lý để đảm bảo rằng nếu một hệ thống gặp sự cố, dữ liệu có thể được khôi phục từ một vị trí khác.
- Sử dụng distributed database (cơ sở dữ liệu phân tán): Các cơ sở dữ liệu phân tán lưu trữ dữ liệu trên nhiều nút khác nhau. Khi một transaction được cam kết, nó được sao chép đến tất cả các nút trong hệ thống. Điều này đảm bảo rằng ngay cả khi một vài nút gặp sự cố, dữ liệu vẫn còn tồn tại trên các nút khác và hệ thống có thể tiếp tục hoạt động.
- Sử dụng Write-Ahead Logging (WAL): Trong kỹ thuật này, tất cả các thay đổi được ghi vào log trước khi thực sự được áp dụng vào cơ sở dữ liệu. Điều này đảm bảo rằng trong trường hợp sự cố, các thay đổi gần đây nhất có thể được khôi phục từ log.
- Triển khai Two-Phase Commit (2PC): Đây là một giao thức được sử dụng trong các hệ thống phân tán để đảm bảo rằng một transaction chỉ được cam kết nếu tất cả các nút tham gia đồng ý. Điều này đảm bảo rằng một transaction chỉ được cam kết khi tất cả các nút có dữ liệu được cập nhật và đồng bộ hóa.
Bạn cần lưu ý rằng không có cách tiếp cận duy nhất nào để đảm bảo tính bền vững mà cần kết hợp nhiều phương pháp, phụ thuộc và nhiều yếu tố như cấu trúc hệ thống, yêu cầu về performance và ngân sách.
4. Tóm tắt
Transaction là một tập hợp các câu query.
ACID là thuộc tính của transaction, bao gồm 4 tính chất:
- Tính nguyên tử (A - Atomicity)
- Tính nhất quán (C - Consistency)
- Tính cô lập (I - Isolation)
- Tính bền vững (D - Durability)
Có 3 hiện tượng đọc (Read phenomenon) phổ biến gây lỗi trong cơ sở dữ liệu là:
- Dirty reads: đọc thay đổi chưa được commit
- Repeatable reads: đọc thay đổi đã được commit
- Phantom reads: đọc thay đổi khi đang có record mới thêm vào cơ sở dữ liệu
Để khắc phục hiện tượng đọc (Read phenomenon) gây ra lỗi, chúng ta có 4 mức độ cô lập (Isolation levels) khác nhau liệt kê theo thứ tự từ mức cô lập cao tới mức cô lập thấp. Mức cô lập càng cao thì hiệu suất của cơ sở dữ liệu càng giảm và ngược lại.
- Serializable: Các transaction thực hiện tuần tự. Đây là cấp độ cô lập cao nhất.
- Repeatable read: Mỗi query trong 1 transaction chỉ nhìn thấy các thay đổi đã commit trước thời điểm BEGIN của transaction.
- Read committed: Mỗi query trong 1 transaction chỉ nhìn thấy các thay đổi đã commit trước khi query bắt đầu.
- Read uncommitted: Transaction có thể đọc những thay đổi chưa được commit hoặc đã được commit bởi những transaction khác. Đây là cấp độ cô lập thấp nhất.
Bài viết liên quan
Giới thiệu Kiến trúc Backend for Frontend (BFF)
Nov 16, 2024 • 10 min read
Flask là gì? Hướng dẫn tạo Ứng dụng Web với Flask
Nov 15, 2024 • 7 min read
Webhook là gì? So sánh Webhook và API
Nov 15, 2024 • 8 min read
Spring Boot là gì? Hướng dẫn Khởi tạo Project Spring Boot với Docker
Nov 14, 2024 • 6 min read
Two-Factor Authentication (2FA) là gì? Vì sao chỉ Mật khẩu thôi là chưa đủ?
Nov 13, 2024 • 7 min read
Test-Driven Development (TDD) là gì? Hướng dẫn thực hành TDD
Nov 13, 2024 • 6 min read