跳至主要内容

Two-Phase Locking Concurrency Control

在上一節中,我們學到了怎麼透過 conflict 來判斷一個 schedule 是否是 serializable,但是在實際的系統中,由於 transaction 會不斷地進入跟離開,且使用者未必會一次性告訴 DBMS 所有需要鎖定的資料,因此我們需要使用 lock 來保證 schedule 是 serializable,而 2PL 就是一種常見的 lock protocol。

Lock

在前面的章節中,我們已經討論過 lock 跟 latch 的差別,這裡我們要深入討論 lock。

  • shared lock (S-Lock): 讀取鎖,可以被多個 transaction 同時持有
  • exclusive lock (X-Lock): 寫入鎖,只能被一個 transaction 持有

DBMS 會有專門的 lock manager 來負責管理整個系統的 locks,只要有 transaction 需要加鎖或是升級鎖時都會向它發出請求,lock manager 會維護一個 lock table,這個 table 上面會記錄所有鎖的資料,lock manager 會依據這些資料來決定是否可以加鎖。

Lock

Two-Phase Locking

2PL 是一種 pessimistic 的 concurrency control protocol,它可以動態決定一個 transaction 是否可以存取資料庫中的某個物件,且不需要事先知道 transaction 的所有操作。

2PL 有兩個階段 :

  • Growing Phase: transaction 會在這一個階段請求需要的鎖,且不會釋放已有的鎖

Growing Phase

  • Shrinking Phase: transaction 只能釋放鎖,不能再請求新的鎖

Shrinking Phase

2PL 本身可以保證 schedule 是 conflict serializability,因為它可以構成無環的 precedence graph,但是卻有可能發生 cascading aborts,範例如下 :

Cascading Aborts

由於 t1 被 abort 了,所以 t2 讀到的東西就變成了 dirty read,因此如果 t1 中止了,我們就需要將其他所有有讀過相同資料的 transaction 都中止,這就是 cascading aborts。

Strong Strict Two-Phase Locking (Rigorous 2PL)

Strong Strict Two-Phase Locking

為了解決這個問題,我們可以使用 2PL 的變體 Strong Strict Two-Phase Locking,在每個 transaction commit 之前,不允許釋放任何鎖。

這麼做的好處是可以避免 cascading aborts,且 rollback 的成本也會比較低,缺點是會限制住 concurrency,因為 transaction 會持有鎖直到 commit。

Example

以下是一個舉例,比較 Non-2PL、2PL、Rigorous 2PL 的差異

假設是 t1 是 A 轉帳給 B 100,t2 是印出 A+B 的餘額

Non-2PL 2PL Rigorous 2PL

Deadlock

在 DBMS 中,當多個交易互相等待對方釋放鎖時,會發生 deadlock,而解決 deadlock 主要可以分為預防跟偵測兩種方式。

Deadlock

Deadlock Detection

DBMS 會動態維護一個 wait-for graph,當發現有 cycle 時就會想辦法打破這個環。

在檢測到 deadlock 時,DBMS 會選擇一個 transaction 當作 victim,然後將它中止或重新啟動,可以依據很多條件來決定要中止哪一個 transaction,例如 transaction 的時間,鎖定了多少資料等等,也可以選擇 rollback 所有或部分的 transaction。

Deadlock Detection

Deadlock Prevention

這種方式會在每次 transaction 嘗試拿到鎖時就進行檢查,並決定中止其中一個 transaction,同時系統會為每個 transaction 設定一個 priority 來幫助決定要中止哪一個 transaction。

Deadlock Prevention

主要有兩種方式 :

  • Wait-Die (Old Waits for Young) : 當請求鎖的交易比持有鎖的交易優先級高時,它會等待,否則持有鎖的交易會被中止
  • Wound-Wait (Young Waits for Old) : 當請求鎖的交易比持有鎖的交易優先級高時,持有鎖的交易會被中止並釋放鎖,否則請求鎖的交易將等待

不管是哪種方式,我們只要能確保方向是一致的,就可以避免 deadlock,而我們也可以適當的提升被中止的 transaction 的 priority 來避免 starvation。

Lock Granularities

前面我們所討論的大部分都是針對一條資料的鎖,但是當我們需要一次性的更新數億條資料時,就需要其他方法來避免紀錄過多的鎖。

Lock Granularities

在 DBMS 中,鎖的粒度 (Lock Granularity) 是指鎖定資料庫中的對象 (如資料庫、表格、頁面、元組或屬性) 的層次和範圍。不同粒度的鎖對並行性和系統性能有不同的影響,因此選擇適當的粒度是設計高效並發控制機制的關鍵。

Intention Lock

為了管理和協調不同層次的鎖,DBMS 使用 Intention Locks 來管理多個 transaction 對資料庫中不同對象的鎖定。 這些鎖是隱性的,並且用來表示其持有更低層級的鎖,以確保不會有衝突。

  • Intention-Shared (IS) : 表示在更低層級上存在共享鎖
  • Intention-Exclusive (IX) : 表示在更低層級上存在獨佔鎖或共享鎖
  • Shared + Intention-Exclusive (SIX) : 表示在當前層級以共享模式鎖定該節點,並且在更低層級存在獨佔鎖

Intention Lock

需要特別注意的是,IS 跟 IX 是可以同時存在的,且 SIX 跟 IS 也是可以同時存在的。

範例如下 :

  • T1 : 掃描 R 中的所有元組,並更新其中的一個元組
  • T2 : 讀取一個元組
  • T3 : 掃描所有元組

Intention Lock Example Intention Lock Example

  • T1 首先會在表中請求 SIX 鎖,並在需要更新的元組上請求 X 鎖
  • T2 會在表中請求 IS 鎖,並在讀取的元組上請求 S 鎖
  • T3 會在表中請求 S 鎖,並且等待 T1 釋放 SIX 鎖後再讀取

References