細粒度鎖的實現之分級鎖的設計實現

2021-09-26 10:40:12 字數 4179 閱讀 8634

在分布式系統中,想必我們經常會看到鎖的應用來保證操作的原子性,使用較簡單的例如物件鎖,單一鎖等等,再高階一點的例如讀寫鎖等等。但是不論是單一鎖或者讀寫鎖,在使用上都具有一定的互斥性。這裡的互斥性指的是當某個鎖持有者持有當前的鎖之後,其它執行緒必須進行阻塞等待操作。這種行為在具有很高workload的系統中,代價還是比較高的。從更深層次來看待這種鎖,它是一種單一粒度,較為粗粒度的鎖設計模式。那麼在實際的應用中,我們是否能夠將這種單一鎖進行優化呢,使之用起來能夠更為的高效。本文筆者將要講述的將是粗粒度鎖的細粒度化改造,改造的實現方式為分級鎖的實現。

為什麼我們這麼強調鎖的細粒度化改造呢?相比於粗粒度鎖,它在使用上能夠帶給系統怎樣的幫助呢?

說到這裡我們不得不談到粗粒度鎖在使用上的一些弊端,典型的一點比如它會阻塞一些毫無關聯的請求操作的處理。比如某個儲存系統在根目錄下有a,b兩個目錄,為了保證系統處理請求操作的原子性,我們用鎖來做其中的控制。如果我用1個鎖來做,則會有一下兩種情況發生:

但其實上面的場景系統在持有鎖的情況去保護a目錄的併發修改卻同樣block住了b目錄的操作,這其實是可以避免的,我們完全可以讓這2個目錄的相關操作併發地執行,然後再用2個對應鎖去保證這2個目錄空間下的請求操作。這樣的話,系統的請求吞吐量將會上公升很多。

在上面的例子中從乙個全域性單一鎖到2個命名空間目錄單獨鎖的拆分,就是鎖細粒化改造的乙個簡單例子。下面本文將要講述的分級鎖的設計部分也是採用了上述的思路,但是額外多了部分的優化改造,使之更適用於實際系統的使用。

本節將要介紹的分級鎖的主要特點在於它包含有多個層級的鎖,在這裡我們以兩級鎖為例,在此鎖內,包含有2個級別鎖:

在分布鎖中,遵守以下規則:

在操作開始前,必須先申請得到top鎖來準備獲取child鎖,在獲取得到child鎖之後,可以再釋放top鎖。這裡的child鎖,可以理解為就是每個分割槽鎖。這裡top鎖的目的是為了保證獲取各個分割槽鎖的原子性。

分級鎖原型定義如下:

/**

* latchlock controls two hierarchical read/write locks:

* the toplock and the childlock.

* typically an operation starts with the toplock already acquired.

* to acquire child lock latchlock will

* first acquire the childlock, and then release the toplock.

*/public

abstract

class

latchlock

public

void

readunlock()

public

void

writelock()

public

void

writeunlock()

}

在分級鎖中,儘管top鎖會是同乙個,但是假設我們獲取的不同的child鎖,其實不會收到top鎖其它執行緒持有的情況。因為其它child鎖被lock之後,top鎖就釋放了,這樣的話其它分級鎖的child鎖的獲取就不會受到影響了。

在這裡top鎖扮演的還是之前全域性同一鎖的角色,但是所鎖住的物件是每個分割槽的例項而不是每乙個具體的操作了。

這裡我們以典型的hdfs fsn全域性單一鎖為例作為top鎖的分級鎖實現:

public

class

inodemaplock

extends

latchlock

private

inodemaplock

(reentrantreadwritelock childlock)

@override

protected

boolean

isreadtoplocked()

@override

protected

boolean

iswritetoplocked()

@override

protected

void

readtopdunlock()

@override

protected

void

writetopunlock()

@override

protected

boolean

hasreadchildlock()

@override

protected

void

readchildlock()

, {}", thread.currentthread().getid(), thread.currentthread().getname());

this

.childlock.

readlock()

.lock()

; namesystem.

getfslock()

.addchildlock

(this);

// log.info("readchildlock: done");

}@override

protected

void

readchildunlock()

, {}", thread.currentthread().getid(), thread.currentthread().getname());

this

.childlock.

readlock()

.unlock()

;// log.info("readchildunlock: done");

}@override

protected

boolean

haswritechildlock()

@override

protected

void

writechildlock()

, {}", thread.currentthread().getid(), thread.currentthread().getname());

this

.childlock.

writelock()

.lock()

; namesystem.

getfslock()

.addchildlock

(this);

// log.info("writechildlock: done");

}@override

protected

void

writechildunlock()

, {}", thread.currentthread().getid(), thread.currentthread().getname());

this

.childlock.

writelock()

.unlock()

;// log.info("writechildunlock: done");

}@override

protected latchlock

clone()

}

在使用分級鎖時,如果遇到可能需要獲取多分割槽(child)鎖時,則要進行多個分割槽child鎖的獲取,之後再釋放top鎖,操作方法如下:

/**

* 獲取多child鎖,keys為write操作涉及到的相關分割槽例項

*/public

void

latchwritelock

(k keys)

assert plock != null :

"plock is null"

; plock.

writetopunlock()

;}

在上面的例子中,遵循的規則如下:

每個partition對應乙個partition鎖(就是本文提到的分級鎖),每個partition鎖包含child鎖和top鎖,top鎖是所有partition鎖共用的乙個鎖,child鎖則是每個partition獨有的。所以我們可看到,分級鎖在多partition情況下可以很好地得到運用。

[1]. . namenode fine-grained locking via metadata partitioning

細粒度鎖的實現之分級鎖的設計實現

在分布式系統中,想必我們經常會看到鎖的應用來保證操作的原子性,使用較簡單的例如物件鎖,單一鎖等等,再高階一點的例如讀寫鎖等等。但是不論是單一鎖或者讀寫鎖,在使用上都具有一定的互斥性。這裡的互斥性指的是當某個鎖持有者持有當前的鎖之後,其它執行緒必須進行阻塞等待操作。這種行為在具有很高workload的...

zookeeper分布式鎖的設計思路與實現

分布式鎖有多種實現方式,比如通過資料庫,redis都可以實現,作為分布式協同工具zookeeper也有著標準的實現方式.設計思路 每個客戶端往 locks下建立臨時有序節點 locks lock 建立成功之後 locks下面會有每個客戶端對應的節點,如 locks lock 0000000001 客...

讀 寫鎖的實現和應用(高併發狀態下的map實現)

程式中涉及到對一些共享資源的讀和寫操作,且寫操作沒有讀操作那麼頻繁。在沒有寫操作的時候,兩個執行緒同時讀乙個資源沒有任何問題,所以應該允許多個執行緒能在同時讀取共享資源。但是如果有乙個執行緒想去寫這些共享資源,就不應該再有其它執行緒對該資源進行讀或寫 譯者注 也就是說 讀 讀能共存,讀 寫不能共存,...