1. Định nghĩa Transaction
Transaction là sự hợp nhất nhiều công việc thành một khối công việc. Khối công việc này được xem là hoàn tất khi tất cả các công việc đều hoàn tất, ngược lại, nếu một trong các công việc thất bại thì khối công việc được xem như thất bại.
Ví Dụ:
Chúng ta cần thực hiện một nghiệp vụ chuyển tiền, chuyển $1000 từ tài khoản A sang tài khoản B, chúng ta cần thực hiện theo 2 bước :
– $1000 sẽ được trừ tại tài khoản A
– $1000 sẽ được cộng vào tài khoản B
Giả sử như chúng ta thực hiện thành công tại bước 1, tuy nhiên tại bước 2 quá trình thực hiện tại bước 2 bị thất bại (lý do mất điện). Vậy đến khi có điện chúng ta sẽ có 2 lựa chọn để thực hiện :
– Quay lui bỏ việc trực hiện chuyển $1000 sang tài khoản A
– Hoặc thực hiện lại việc cộng tiền vào tài khoản B.
Sử dụng Transaction (giao tác) sẽ tránh được những vấn đề đã được cập nhật ở trên. Lúc này nghiệp vụ chuyển tiền sẽ được coi là một transaction. Thông thường Transaction thường được sử dụng với CSDL.
Kết quả khi thực hiện transaction
– Thành công -> Transaction đã được chuyển giao (Commited)
– Không thành công -> Transaction bị hủy bỏ
Một transaction đã được chuyển giao thì không thể bị hủy bỏ. Nếu transaction có sai sót thì cần một transaction khác điều chỉnh lại.
2. Sử dụng transaction
Transaction được sử dụng trong các ngữ cảnh khi có nhiều các thao tác được thực thi như một khối công việc. Thông thường Transaction được thực thi trong các tình huống sau :
– Khi có nhiều dòng dữ liệu cần được chỉnh sửa, nếu quá trình thực hiện bị lỗi tại một dòng nào đó, thì toàn bộ công việc chỉnh sửa tại các dòng dữ liệu này coi như thất bại.
– Khi chỉnh sửa thông tin của một bảng, bảng này có ánh xạ thông tin tới nhiều các bảng khác.
– Khi nhiều CSDL cần chỉnh sửa thông tin cùng một thời điểm.
– Khi nhiều dữ liệu bị thay đổi trong CSDL từ nhiều Server khác nhau.
3. Các thuộc tính cơ bản (ACID) của Transaction
– Atomicity (Tính nguyên tố): Tất cả các yêu cẩu của một transaction phải hoàn tất , nếu không transaction bị hủy bỏ. Ví dụ: Nếu transaction T1 có 4 yêu cầu SQL, tất cả 4 yêu cầu phải hoàn tất, ngược lại toàn bộ transaction bị hủy bỏ.
– Consitency (Tính nhất quán): Một transaction phải chuyển CSDL từ trạng thái nhất quán này sang trạng thái nhất quán khác.
– Isolation (Tính cô lập): Dữ liệu được dùng trong lúc thực thi một transaction không thể được dùng với một transaction khác cho đến khi transaction đầu tiên hoàn tất. Ví dụ : Nếu transaction T1 đang thực thi và đang sử dụng dữ liệu X thì dữ liệu X không thể bị truy xuất bởi bất khì transaction nào khác (T2… Tn) cho đến khi transaction T1 kết thúc.
– Durability (Tính bền vững): Những thay đổi của CSDL do transaction thực hiện thành công là bền vững, không bị mất kể cả khi có lỗi xảy ra sau đó.
4. Các giai đoạn thực thi transaction
– Trước khi bắt đầu một transaction, CSDL phải ở một trạng thái nhất quán.
– Ứng dụng báo hiệu bắt đầu một transaction tùy theo mỗi loại transation.
– Ứng dụng bắt đầu sử đổi CSDL, CSDL có thể ở trạng thái trung gian không nhất quán.
– Khi ứng dụng hoàn tất thành công mọi sửa đổi, CSDL sẽ chuyển về lại trạng thái nhất quán. Mọi sửa đổi sẽ trở thành cố định trong CSDL.
– Khi ứng dụng gặp lỗi, nó sẽ hủy bỏ mọi sửa đổi dữ liệu, và đưa CSDL trở lại trạng thái nhất quán trước khi bắt đầu transaction.
– Khi một transaction bắt đầu bởi một người dùng hay một chương trình, các lệnh trong transaction sẽ được thực hiện tuần tự cho đến khi 1 trong 4 sự kiện sau xảy ra :
- Gặp lệnh Commit: Tất cả mọi thay đổi sẽ được ghi vào CSDL, lệnh Commit kết thúc thành công một transaction.
- Gặp lệnh Rollback: Tất cả mọi thay đổi sẽ bị hủy bỏ, CSDL quay về trạng thái ổn định trước khi bắt đầu transaction.
- Kết thúc chương trình thành công: Mọi thay đổi được ghi vào CSDL, hành động này tương tự như Commit.
- Chương trình kết thúc bất thường: Tương đương với lệnh Rollback.
5. Giới thiệu transaction ở mức độ cô lập (Isolation Level)
5.1. Những vấn đề xảy ra khi truy xuất dữ liệu đồng thời
– Mất dữ liệu cập nhật (Lost update): Tình trạng này xảy ra khi có nhiều hơn một giao tác cùng thực hiện cập nhật trên 1 đơn vị dữ liệu. Khi đó, tác dụng của giao tác cập nhật thực hiện sau sẽ đè lên tác dụng của thao tác cập nhật trước.
– Dirty Read: Đọc dữ liệu chưa được chuyển giao (Uncommited Dependence – Dữ liệu chưa được chuyển giao):
- Xảy ra khi một transaction thứ 2 chọn một hàng chưa được cập nhật bởi một transaction khác.
- Transaction thứ 2 đọc dữ liệu lúc chưa chuyển giao và có thể bị thay đổi bởi transaction đang thực hiện việc cập nhật.
- Ví dụ: 2 Transaction T3, T4 thực hiện đồng thời, T3 rút $20, T4 gửi thêm $200 nhưng lại hủy bỏ sau đó.
– Unrepeatable data – Giao tác không thể lặp lại:
- Tình trạng này xảy ra khi một giao tác T1 vừa thực hiện xong thao tác đọc trên một đơn vị dữ liệu (nhưng chưa commit) thì giao tác khác (T2) lại thay đổi (ghi) trên đơn vị dữ liệu này. Điều này làm cho lần đọc sau đó của T1 không còn nhìn thấy dữ liệu ban đầu nữa.
- Ví dụ: Giao tác T5 và T6 cùng thực hiện đồng thời, T5 rút tiền, T6 tính tổng số dư của 3 tài khoản x,y,z. Khi kết thúc 2 giao tác, kết quả T6 không chính xác.
– Phantom – Bóng ma:
- Là tình trạng mà một giao tác đang thao tác trên một tập dữ liệu nhưng giao tác khác lại chèn thêm các dòng dữ liệu vào tập dữ liệu mà giao tác kia quan tâm.
5.2. Giải pháp xử lý
– Thực hiện cơ chế Transaction và cơ chế khóa.
– Trước khi transaction đọc hoặc chỉnh sửa dữ liệu, nó cần được bảo vệ và tránh ảnh hưởng của các transaction khác đang chỉnh sửa cùng dữ liệu.
– Transaction yêu cầu khóa dữ liệu đang dùng.
– Có nhiều Mode khóa khác nhau phụ thuộc vào mức độ phụ thuộc dữ liệu của transaction.
– Sẽ không có transaction nào được cấp khóa nếu gây xung đột với mode khóa đã được cấp trên cùng dữ liệu cho một transaction khác trước đó.
– Nếu transaction yêu cầu một mode khóa xung đột, DBMS sẽ bắt transaction này dừng lại cho đến khi transaction trước đó được giải phóng.
– Tất cả các khóa sẽ được giải phóng khi transaction hoàn thành.
– Hầu hết các DBMS đa người dùng đều tự động thực thi các thủ tục khóa.
– Lock Manager có nhiệm vụ gán và tạo chính sách khóa cho các transaction.
– Khi cần phải sử dụng câu lệnh SQL, query processor sẽ xác định tài nguyên nào được truy xuất, loại khóa nào cần dùng, thiết lập mức độ cô lập (Isolation level) cho transaction. Kế đến query processor yêu cầu một khóa phù hợp từ lock manager. Lock Manager sẽ cấp khóa nếu không có xung đột.
5.3. Cấp độ khóa
Có các cấp độ khóa như sau :
– Mức Database
– Mức Table
– Mức Page
– Mức Row
– Mức Field
5.4. Các loại khóa
Bất kể mức khóa nào DBMS có thể dùng 2 loại khóa khác nhau :
– Binary: Có 2 trạng thái Lock(1) và Unlock (0)
– Shared / Exclusive:
- Exclusive Lock: Tồn tại việc truy xuất được dành riêng cho transaction đang khóa đối tượng. Khóa Exclusive phải được dùng khi có thể xảy ra xung đột.
- Shared Lock: Tồn tại khi các transaction có chung quyền đọc, Shared lock không tạo ra xung đột chừng nào tất cả các transaction đồng thời chỉ đọc.
- Share Lock được tạo ra khi transaction muốn đọc dữ liệu từ DB và không có Exclusive nào đang nắm giữ trên mục dữ liệu đó.
- Exclusive lock được tạo ra khi transaction muốn cập nhật mục dữ liệu, hiện thời chưa có khóa nào đang nắm giữ mục dữ liệu.
- Mỗi khóa có 3 trạng thái: Unlocked, Shared (Read) và Exclusive (Write).
5.5. Xử lý khóa ở mức độ cô lập
– Read Uncommitted – đọc dữ liệu chưa chuyển giao:
- Đặc điểm:
– Không thiết lập Shared Lock trên những đơn vị dữ liệu cần đọc. Do đó không phải chờ khi đọc dữ liệu (kể cả khi dữ liệu đang bị lock bởi giao tác khác)
– Vẫn tạo Exclusive Lock trên đơn vị dữ liệu được ghi, Exclusive Lock được giữ cho đến hết giao tác
- Ưu điểm
– Tốc độ xử lý rất nhanh
– Không cản trở những giao tác khác thực hiện việc cập nhật dữ liệu
- Nhược điểm
– Có khả năng xảy ra mọi vấn đề khi xử lý đồng thời :
- Dirty Reads
- Unrepeatable Reads
- Phantoms
- Lost Updates
- Read Commited
- Đặc điểm
– Đây là mức độ cô lập mặc định của SQL Server
– Tạo Shared Lock trên đơn vị dữ liệu được đọc, Shared Lock được giải phóng ngay sau khi đọc xong dữ liệu
– Tạo Exclusive Lock trên đơn vị dữ liệu được ghi, Exclusive Lock được giữ cho đến hết giao tác
- Ưu điểm
– Giải quyết vấn đề Dirty Reads.
– Shared Lock được giải phóng ngay, không cần phải giữ cho đến hết giao tác nên không cản trở nhiều đến thao tác cập nhật của các giao tác khác.
- Nhược điểm
– Chưa giải quyết được vấn đề Unrepeatable Reads, Phantoms,Lost Updates
– Phải chờ nếu đơn vị dữ liệu cần đọc đang được giữ khoá ghi (xlock)
- Repeatable Read
- Đặc điểm
– Tạo Shared Lock trên đơn vị dữ liệu được đọc và giữ shared lock này đến hết giao tác => Các giao tác khác phải chờ đến khi giao tác này kết thúc nếu muốn cập nhật, thay đổi giá trị trên đơn vị dữ liệu này .
– (Repeatable Read = Read Committed + Giải quyết Unrepeatable Reads).
– Tạo Exclusive Lock trên đơn vị dữ liệu được ghi, Exclusive Lock được giữ cho đến hết giao tác.
- Ưu điểm
– Giải quyết vấn đề Dirty Reads và Unrepeatable Reads
- Nhược điểm
– Chưa giải quyết được vấn đề Phantoms, do vẫn cho phép insert những dòng dữ liệu thỏa điều kiện thiết lập shared lock
– Phải chờ nếu đơn vị dữ liệu cần đọc đang được giữ khoá ghi (xlock)
– Shared lock được giữ đến hết giao tác ==> cản trở việc cập nhật dữ liệu của các giao tác khác
- Serializable
- Đặc điểm
– Tạo Shared Lock trên đơn vị dữ liệu được đọc và giữ shared lock này đến hết giao tác => Các giao tác khác phải chờ đến khi giao tác này kết thúc nếu muốn cập nhật, thay đổi giá trị trên đơn vị dữ liệu này .
– Không cho phép Insert những dòng dữ liệu thỏa mãn điều kiện thiết lập Shared Lock (sử dụng Key Range Lock) ==> Serializable = Repeatable Read + Giải quyết Phantoms
– Tạo Exclusive Lock trên đơn vị dữ liệu được ghi, Exclusive Lock được giữ cho đến hết giao tác.
- Ưu điểm
– Giải quyết thêm được vấn đề Phantoms
- Nhược điểm
– Phải chờ nếu đơn vị dữ liệu cần đọc đang được giữ khoá ghi (xlock)
– Cản trở nhiều đến việc cập nhật dữ liệu của các giao tác khác
6. Sử dụng Transaction với ADO.NET:
– Bước 1: Thêm gói chứa đối tượng Transaction:
using System.Data.SqlClient;
– Bước 2: Kết nối đến CSDL
SqlConnection cnn = new SqlConnection(<chuỗi kết nối CSDL>); try { cnn.Open(); //mở kết nối } catch(Exception e) { //xử lý lỗi kết nối hỏng }
– Bước 3: Thi hành câu lệnh SQL sử dụng đối tượng SqlCommand, sử dụng transaction
//tạo đối tượng cmd mới SqlCommand cmd = new System.Data.SqlClient.SqlCommand(); //loại lệnh: câu lệnh SQL cmd.CommandType = CommandType.Text; //gán câu lệnh SQL cmd.CommandText = <câu lệnh SQL cập nhật dữ liệu>; //ấn định kết nối CSDL cho đối tượng cmd cmd.Connection = cnn; //khai báo một transaction SqlTransaction transaction; //bắt đầu quá trình quản lý transaction transaction = cnn.BeginTransaction(); //gắn transaction với đối tượng cmd cmd.Transaction = transaction; try { cmd.ExecuteNonQuery(); //cam kết thực hiện thành công transaction.Commit(); } catch(Exception e) //nếu xảy ra lỗi { //hiển thị thông báo lỗi tại đây transaction.Rollback(); //quay lùi }