.2 Thao tác khóa và mở khóa đối với 2 chế độ khóa shared/exclusive

Một phần của tài liệu (LUẬN VĂN THẠC SĨ) Quản lý giao tác trong CSDL quan hệ và phân tán (Trang 52)

shared/exclusive

Các khóa phương thức Shared có thể được giữ đồng thời trên một mục dữ liệu. Một khóa exclusive đến sau phải chờ đến tận khi tất cả các khóa phương thức Shared đến trước được giải phóng.

Một giao tác yêu cầu một khóa Shared trên mục dữ liệu X bằng cách thực hiện chỉ thị lock-Shared(X), yêu cầu một khóa Exclusive thông qua chỉ thị lock-EXCLUSIVE(X). Một mục dữ liệu X có thể được tháo khóa thông qua chỉ thị unlock(X).

Để truy cập một mục dữ liệu, giao tác Ti đầu tiên phải khóa mục này. Nếu mục này đã bị khóa bởi một giao tác khác ở phương thức không tương thích, bộ điều khiển cạnh tranh sẽ không cấp khóa cho đến tận khi tất cả các khóa không tương thích bị giữ bởi các giao tác khác được mở. Như vậy Ti phải chờ đến tận khi tất cả các khóa không tương thích bị giữ bởi các giao tác khác được giải phóng.

Giao tác Ticó thể tháo khóa một mục dữ liệu mà nó đã khóa trước đây. Một giao tác cần thiết phải giữ một khóa trên một mục dữ liệu chừng nào mà nó còn truy cập mục này. Hơn nữa, đối với một giao tác việc mở khóa ngay Sau truy cập cuối cùng đến mục dữ liệu không luôn luôn là điều mong muốn vì như vậy tính khả năng tuần tự có thể không được đảm bảo [2]. Để minh hoạ cho tình huống này, ta xét ví dụ Sau: A và B là hai tài khoản có thể được truy cập bởi các giao tác T1và T2. Giao tác T1 chuyển 50$ từ tài khoản B sang tài khoản A và được xác định như sau :

T1: Lock-EXCLUSIVE(B); Read_item(B); B:=B-50; Write_item(B); Unlock(B); Lock-EXCLUSIVE(A); Read_item(A); A:=A+50; Write_item(A); Unlock(A);

Giao tác T2 hiển thị tổng số lượng tiền trong các tài khoản A và B (A + B) và được xác định như sau;

T2: Lock-SHARED(A); Read_item(A); Unlock(A); Lock-SHARED(B); Read_item(B); Unlock(B); Display(A+B);

Giả sử giá trị của tài khoản A và B tương ứng là 100$ và 200$. Nếu hai giao tác này thực hiện tuần tự, hoặc theo thứ tự T1, T2 hoặc theo thứ tự T2, T1, và khi dó T2sẽ hiển thị giá trị 300$. Tuy nhiên nếu các giao tác này thực hiện đồng thời, giả sử theo trình tự Schedule-1, trong trường hợp như vậy giao tác T2 sẽ hiển thị giá trị 250$ --- một kết quả không đúng. Lý do của sai lầm này là do giao tác T1 đã mở khóa mục B quá sớm và T2 đã tham khảo một trạng thái không nhất quán.

Trình tự Schedule 1 bày tỏ các hành động được thực hiện bởi các giao tác cũng như các thời điểm khi các khóa được cấp bởi bộ quản trị điều khiển đồng thời. Giao tác đưa ra một yêu cầu khóa không thể thực hiện hành động kế tiếp của mình đến tận khi khóa được cấp bởi bộ quản trị điều khiển đồng thời; do đó, khóa phải được cấp trong khoảng thời gian giữa hoạt động yêu cầu khóa và hành động sau của giao tác. Sau này ta sẽ luôn giả thiết khóa được cấp cho giao tác ngay trước hành động kế và như vậy ta có thể bỏ qua cột bộ quản trị điều khiển đồng thời trong bảng

T 1 T 2 Bộ quản trị điều khiển đồng thời Lock-EXCLUSIVE(B) Grant-EXCLUSIVE(B,T 1) Read_item(B) B:=B-50 Write_item(B) Unlock(B) Lock-SHARED(A) Grant-SHARED(A,T 2) Read_item(A) Unlock(A) Lock-SHARED(B) Grant-SHARED(B,T 2) Read_item(B) Unlock(B) Display(A+B) Lock-EXCLUSIVE(A) Grant-EXCLUSIVE(A,T 1) Read_item(A) A:=A+50 Write_item(A) Unlock(A) Schedule-1

Bây giờ, giả sử rằng mở khóa bị làm trễ đến cuối giao tác. Giao tác T3 tương ứng với T1 với mở khóa bị làm trễ được định nghĩa như sau:

T3 Lock-EXCLUSIVE(B); Read_item(B); B:=B-50; Write_item(B); Lock-EXCLUSIVE(A); Read_item(A); A:=A+50; Write_item(A); Unlock(B);

Giao tác T4 tương ứng với T2 với mở khóa bị làm trễ được xác định như sau: T4: Lock-SHARED(A); Read_item(A); Lock-SHARED(B); Read_item(B); Display(A+B); Unlock(A); Unlock(B);

Các trình tự có thể trên T3 và T4 không để cho T4 hiển thị trạng thái không nhất quán.

Tuy nhiên, sử dụng khóa có thể dẫn đến một tình huống không mong đợi. Ta hãy xét trình tự bộ phận Schedule-2 trên T3 và T4 sau:

T 3 T 4 Lock-EXCLUSIVE(B) Read_item(B) B:=B-50 Write_item(B) Lock-SHARED(A) Read_item(A) Lock-SHARED(B) Lock-EXCLUSIVE(A) Schedule-2

Do T3 giữ một khóa phương thức Exclusive trên B, nên yêu cầu một khóa phương thức Shared của T4 trên B phải chờ đến khi T3 mở khóa. Cũng vậy, T3 yêu cầu một khóa Exclusive trên A trong khi T4 đang giữ một khóa Shared trên nó và như vậy phải chờ. Ta gặp phải tình huống trong đó T3 chờ

đợi T4 đồng thời T4 chờ đợi T3 -- một sự chờ đợi vòng tròn -- và như vậy không giao tác nào có thể tiến triển. Tình huống này được gọi là khoá chết (deadlock). Khi tình huống khoá chết xảy ra hệ thống buộc phải cuộn lại một trong các giao tác. Mỗi khi một giao tác bị cuộn lại, các mục dữ liệu bị khóa bởi giao tác phải được mở khóa và nó trở nên sẵn có cho giao tác khác, như vậy các giao tác này có thể tiếp tục được sự thực hiện của nó.

Nếu ta không sử dụng chốt hoặc tháo chốt hạng mục dữ liệu ngay khi có thể sau đọc hoặc viết hạng mục, ta có thể rơi vào trạng thái không nhất quán. Mặt khác, nếu ta không tháo chốt một hạng mục dữ liệu trước khi yêu cầu một chốt trên một hạng mục khác, khóa chết có thể xảy ra. Có các phương pháp tránh khóa chết trong một số tình huống, tuy nhiên nói chung khóa chết là khó tránh khi sử dụng khóa nếu ta muốn tránh trạng thái không nhất quán. Khóa chết được ưa thích hơn trạng thái không nhất quán vì chúng có thể điều khiển được bằng cách cuộn lại các giao tác trong khi đó trạng thái không nhất quán có thể dẫn đến các vấn đề thực tế mà hệ CSDL không thể điều khiển [1].

Ta sẽ yêu cầu mỗi giao tác trong hệ thống tuân theo một tập các quy tắc, chỉ định khi một giao tác có thể khóa và mở khóa mỗi một trong các mục dự liệu. Giao thức khóa hạn chế số các trình tự có thể.

Khi sử dụng lược đồ khóa shared/exclusive, hệ thống phải tuân theo các quy tắc sau [1,10]:

(1) Một giao tác T phải đưa ra thao tác read_lock(X) hoặc write_lock(X) trước khi có bất kỳ thao tác read_item(X) nào trong T được thực hiện.

(2) Một giao tác T phải đưa ra thao tác write_item(X) trước khi có bất kỳ thao tác write_item(X) nào trong T được thực hiện.

(3) Một giao tác T sẽ đưa ra một thao tác unlock(X) sau khi tất tả các thao tác read_item(X) và write_item(X) trong T được thực hiện xong.

(4). Một giao tác T sẽ không đưa ra một thao tác read_lock(X) nếu nó giữ khóa shared (read) hoặc exclusive (write) trên mục dữ liệu X. (5) Một giao tác T sẽ không đưa ra một thao tác write_lock(X) nếu nó

giữ khóa shared hoặc khóa exclusive trên mục dữ liệu X.

(6) Một giao tác T sẽ không đưa ra một thao tác unlock(X) nếu nó không giữ khóa shared hoặc khóa exclusive trên mục dữ liệu X.

Chuyển đổi khóa (Conversion of locks) Việc chuyển đổi khóa cũng được mong đợi để giảm bớt các điều kiện 4 và 5 trong danh sách theo trình tự cho phép chuyển đổi khóa; có nghĩa là một giao tác giữ một khóa trên mục dữ liệu X được cho phép dưới các điều kiện đảm bảo để chuyển đổi khóa từ một trạng thái khóa sang trạng thái khác. Ví dụ, khả năng để một giao tác T đưa ra một thao tác read_lock(X) và sau đó nâng cấp khóa bằng cách đưa ra thao tác write_lock(X). Nếu T là một giao tác chỉ giữ khóa đọc trên X thì tại một thời điểm nào đó nó đưa ra thao tác write_lock(X) và khóa có thể được nâng cấp; ngược lại, giao tác phải đợi. Cũng có thể một giao tác T đưa ra một thao tác write_lock(X) sau khi hạ cấp khóa bằng cách đưa ra thao tác read_lock(X). Khi sử dụng khóa nâng cấp hoặc hạ cấp, bảng khóa phải bao gồm các nhận dạng giao tác trong cấu trúc bản ghi của mỗi khóa (trong trường locking_transaction(s)) để lưu giữ thông tin các giao tác giữ khóa trên mục dữ liệu. Mô tả các thao tác read_lock(X) và write_lock(X) trong hình 2.2 phải được thay đổi tương ứng [1].

Sử dụng khóa nhị phân hoặc khóa shared/Exclusive trong giao tác như được mô tả ở trên không đảm bảo tính khả năng tuần tự của các lịch biểu.

Hình 2.3 chỉ ra một ví dụ với các quy tắc khóa cho trước nhưng kết quả có thể là một lịch biểu không thể tuần tự. Điều này là do trong hình 2.3(a) các mục Y trong T1 và X trong T2 được mở quá sớm. Điều này kéo theo một lịch biểu chẳng hạn một lịch biểu được chỉ ra trong hình 2.3(c) xuất hiện, nó không phải là lịch biểu tuần tự do đưa ra kết quả không đúng. Để đảm bảo tính tuần tự, chúng ta phải kéo thêm một giao thức liên quan đến khả năng thao tác khóa và mở khóa trong mọi giao tác. Giao thức khóa 2 kỳ (Two - phase locking) (a) T1 T2 Read_lock(Y); Read_lock(X); Read_item(Y); Read_item(X); Unlock(Y); Unlock(X); Write_lock(X); Write_lock(Y); Read_item(X); Read_item(Y); X:=X+Y; Y:=X+Y; Write_item(X); Write_item(Y); Unlock(X); Unlock(Y);

(b) Với giá trị ban đầu: X = 20 và Y = 30

Kết quả của lịch biểu tuần tự T1 kéo theo bởi T2 X = 50, Y = 80

Kết quả của lịch biểu tuần tự T2 kéo theo bởi T1 X = 70, Y = 50

(c) T1 T2 Read_lock(Y); Read_item(Y); Unlock(Y); Read_lock(X); Read_item(X); Unlock(X); Write_lock(Y); Read_item(Y); Y:=X+Y; Write_item(Y); Unlock(Y); Write_lock(X); Read_item(X); X:=X+Y; Write_item(X); Unlock(X);

Kết quả của lịch biểu: X = 50, Y = 50 (không tuần tự) Hình 2.3 Giao tác không tuân theo khóa 2 kỳ

(a) 2 giao tác T1 và T2

(b) Các kết quả có thể của lịch biểu tuần tự T1 và T2 (c) Lịch biểu S không tuần tự sử dụng khóa

2.1.2 Khóa 2 kỳ (Two - phase locking)

Giao thức khóa 2 kỳ là một giao thức đảm bảo tính khả năng tuần tự. Một giao tác được hiểu theo giao thức khóa 2 kỳ nếu mọi thao tác khóa (read_lock, write_lock) đi trước thao tác mở khóa đầu tiên trong giao tác. Như vậy giao thức này yêu cầu mỗi một giao tác phát ra yêu cầu khóa và mở khóa chia ra thành 2 kỳ [1]:

(1) Kỳ xin khóa (Growing phase): Một giao tác có thể nhận được các khóa nhưng nó không thể mở bất kỳ khóa nào

(2) Kỳ mở khóa (Shrinking phase): Một giao tác có thể mở các khóa nhưng không thể nhận được một khóa mới nào.

Nếu chuyển đổi khóa được cho phép thì nâng cấp khóa (từ read_lock thành write_lock) phải được hoàn thành trong kỳ xin khóa, và hạ cấp khóa (từ write_lock xuống read_lock) phải được hoàn thành trong kỳ mở khóa. Do đó, thao tác read_lock(X) mà từ write_lock(X) hạ xuống chỉ có thể xuất hiện trong kỳ mở khóa [1].

Các giao tác T1 và T2 trong hình 2.3(c) không theo giao thức khóa 2 kỳ. Điều này là do thao tác write_lock(X) kéo theo thao tác unlock(Y) trong T1, tương tự, thao tác write_lock(Y) kéo theo thao tác unlock(X) trong T2. Nếu tuân theo giao thức khóa 2 kỳ, các giao tác có thể được viết lại thành T1’ và T2’ như trong hình 2.4. Lịch biểu được chỉ ra trong hình 2.3(c) không được cho phép với T1’ và T2’ dưới các quy tắc khóa. Điều này là do T1’ sẽ đưa ra thao tác write_lock(X) của nó trước khi mở khóa mục dữ liệu Y; do đó, khi T2’ đưa ra thao tác read_lock(X), nó phải đợi cho đến khi T1’ thay thế khóa bằng cách đưa ra một thao tác unlock(X) trong lịch biểu.

mục dữ liệu X sau khi chấm dứt sử dụng nó nếu như sau đó T phải khóa thêm mục dữ liệu Y hoặc ngược lại, T phải khóa thêm mục dữ liệu Y trước khi nó có thể giải phóng X. Vì thế, X phải được khóa như cũ bởi T cho đến khi tất cả các mục dữ liệu mà giao tác cần đọc hoặc ghi đã bị khóa; chỉ có như thế T mới có thể giải phóng X. Trong khi đó, giao tác khác yêu cầu truy cập X có thể bị buộc phải đợi dù là T đã hoàn thành xong với mục dữ liệu X; ngược lại, nếu Y bị khóa sớm hơn, giao tác khác yêu cầu truy cập Y sẽ bị buộc phải đợi dù cho T chưa sử dụng đến Y. Đây là cái giá cho sự đảm bảo tính khả năng tuần tự của mọi lịch biểu [1].

Có một số sự khác nhau về khóa 2 kỳ (2PL). Kỹ thuật vừa được mô tả được biết đến như basic 2PL. Một sự khác biệt nữa được biết đến như

conservative 2PL (hoặc static 2PL) yêu cầu một giao tác khóa tất cả các mục mà nó truy cập trước khi giao tác bắt đầu thực thi bằng cách khai báo trước

read-setwrite-set, read-set là tập tất cả các mục mà giao tác đọc và write- set là tập tất cả các mục mà giao tác ghi. Nếu bất kỳ mục nào cần thiết được khai báo trước không thể bị khóa thì giao tác không khóa bất kỳ mục dữ liệu nào, nó đợi cho đến khi tất cả các mục dữ liệu sẵn sàng khóa. Conservation 2PL là một giao thức khóa chết không ràng buộc. Tuy nhiên, trong thực tế nó khó sử dụng do cần khai báo trước tập các mục giao tác đọc và tập các mục giao tác ghi [1,2].

Trong thực tế, 2PL phổ biến nhất là strict 2PL, một giao tác không giải phóng bất kỳ khóa exclusive (write) nào của nó cho đến khi nó xác nhận hoàn thành hoặc xác nhận hủy bỏ. Do đó, giao tác khác không thể đọc hoặc ghi một mục dữ liệu đã được ghi bởi T cho đến khi T được xác nhận hoàn thành. Một biến thể khác hạn chế hơn của strict 2PL là rigorous 2PL, giao tác T không giải phóng bất kỳ khóa exclusive hoặc khóa shared nào của nó cho đến khi nó xác nhận hoàn thành hoặc hủy bỏ, và bởi vậy nó dễ thực thi hơn strict 2PL.

Sự khác nhau giữa conservative 2PLrigonous 2PL; conservative 2PL phải khóa tất cả các mục dữ liệu trước khi nó bắt đầu thực hiện.

Trong nhiều trường hợp, hệ thống con điều khiển đồng thời chịu trách nhiệm tạo ra các yêu cầu read_lock và write_lock. Ví dụ, cho rằng hệ thống tuân theo giao thức strict 2PL, Khi đó, mỗi lần giao tác T đưa ra một thao tác read_item(X), hệ thống thay mặt cho giao tác T gọi read_lock(X) vì lợi ích của giảo tác T. Nếu trạng thái của LOCK(X) là write_locked bởi một số giao tác T’ khác, hệ thống đặt giao tác T vào hàng đợi đợi mục dữ liệu X; ngược lại, nó chấp nhận yêu cầu read-lock(X) và cho phép thực thi tao tác read_item(X) của T. Theo cách khác, nếu giao tác T đưa ra một thao tác write_item(X), hệ thống thay mặt cho giao tác T gọi write_lock(X) vì lợi ích của giảo tác T. Nếu trạng thái của LOCK(X) là write_locked hoặc read_locked bởi một số giao tác T’ khác, hệ thống đặt giao tác T vào hàng đợi đợi mục dữ liệu X; nếu trạng thái của LOCK(X) là read_locked và chỉ có giao tác nào của T đang giữ khóa đọc trên X, hệ thống nâng cấp khóa thành write_locked và cho phép thao tác write_item(X); cuối cùng, nếu trạng thái của LOCK(X) = unlocked, hệ thống chấp nhận yêu cầu write_lock(X) và cho phép thực thi thao tác write_item(X). Sau mỗi hành động, hệ thống phải cập nhật bảng khóa tương ứng [1].

2.1.3 Khóa chết và xử lý khóa chết

Mặc dù khóa 2 kỳ đảm bảo tính khả năng tuần tự. Nó không thừa nhận mọi lịch biểu đều có khả năng có thể tuần tự, có nghĩa là có một số lịch biểu có thể tuần tự sẽ bị ngăn cấm bởi giao thức. Hơn nữa, sử dụng khóa có thể là nguyên nhân của vấn đề: khóa chết (deadlock) và sự chết đói (starvation).

Khóa chết xuất hiện khi mỗi giao tác T trong một tập hợp có 2 hay nhiều giao tác đạng đợi cùng mục dữ liệu bị khóa bởi giao tác T’ khác. Do đó, mỗi

giao tác trong tập hợp giao tác trên hàng đợi, đợi một giao tác khác trong tập giải phóng khóa trên mục dữ liệu [2].

Một hệ thống ở trạng thái khóa chết nếu tồn tại một tập hợp các giao tác sao cho mỗi giao tác trong tập hợp đang chờ một giao tác khác trong tập hợp. Chính xác hơn, tồn tại một tập các giao tác {T0 , T2, ..., Tn} sao cho T0 đang chờ một mục dữ liệu được giữ bởi T1, T1 đang chờ một mục dữ liệu đang bị giữ bởi T2, ..., Tn-1 đang chờ một mục dữ liệu được giữ bởi Tn và Tn đang chờ một mục dữ liệu đang bị T0 chiếm. Không một giao tác nào có thể tiến triển

Một phần của tài liệu (LUẬN VĂN THẠC SĨ) Quản lý giao tác trong CSDL quan hệ và phân tán (Trang 52)

Tải bản đầy đủ (PDF)

(96 trang)