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.
Khóa 2 kỳ có thể hạn chế số lượng giao tác đồng thời mà có thể xuất hiện trong một lịch biểu. Điều này là do một giao tác T không thể giải phóng
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-set và write-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 2PL và rigonous 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].