KV系統實現并發鎖
在key-value系統中緩存了網絡服務器上一個重要的ticket,這個ticket用來授權。在一定的時間周期7200s里更新。現需要實現一個CGI提供給前端獲取這個ticket,CGI訪問量為每天百萬pv左右。
假設某一時刻ticket要過期時有A,B兩個請求。A請求過來發現ticket過期,開始從網絡服務器上獲取最新的ticket并寫入KV,同時服務器更新自己存儲的ticket。而B恰好在A寫入前讀出了ticket,此時服務器上的ticket和A同步了。但B的ticket是過期的,這會導致用B獲取的ticket去請求資源時失敗。
因而需要讀寫分離。其實讀寫分離也意味著并發鎖轉移,從可能幾K并發爭鎖減少到幾個并發爭鎖。同時在CGI中降低了加鎖成本。
思路如下:
- CGI只進行讀KV操作
- 實現一個daemon程序,只負責每隔固定周期往KV更新ticket。用一個并發鎖去控制寫入時的資源競爭。 </ul>
- daemon進程占有寫鎖,掛掉后死鎖
- daemon進程掛掉后KV不會更新 </ul>
- 3個進程爭寫鎖,爭到的進程為主進程,每隔1s刷新KV中daemon_mutex的timestamp字段,相當于心跳數據。每隔15分鐘更新KV中的ticket。(ticket更新不用太頻繁)
- 2個從進程每隔1s讀KV中daemon_mutex的timestamp字段,若time(NULL) - kv.update_time() > 15說明主進程掛掉了,2個從進程開始爭鎖,搶到的進程升級為主進程。重復以上。 </ol>
KV實現并發鎖
往KV里添加一個字段daemon_mutex,對應的value為pid + timestamp。
daemon可能會掛掉。掛掉會導致兩方面問題
因此可以啟動三個daemon進程,相互監督。
步驟如下:
分布式系統中心心跳協議的設計可以參見Linux多線程服務端編程。
Chubby技術架構
如果大家熟悉 ZooKeeper(雅虎公司對Chubby的開源實現) 和 Chubby 就會發現上面的并發鎖設計和 Chubby 一樣。
一個典型的 Chubby 集群 通常由5臺服務器組成。這些副本服務器采用 Paxos 協議,通過投票的方法來選舉產生一個獲得過半投票的服務器作為 Master。只有 Master 服務器才能對數據庫進行寫操作,而其他的服務器都是使用 Paxos 協議從 Master 服務器上同步數據庫數據的更新。
在Chubby中,任意一個數據節點都可以充當一個讀寫鎖來使用:一種是單個客戶端以排他(寫)模式持有這個鎖,另一種則是任意數目的客戶端以共享(讀)模式持有這個鎖。
Memcache實現并發鎖
memcache也可以利用add去實現并發鎖,主要是通過add的原子性來判斷是否要執行關鍵代碼。
if (memcache.get(key) == null) { // 設置過期時間防止持有寫鎖的進程死鎖 if (memcache.add(key_mutex, 3 * 60 * 1000) == true) { //主進程執行流,當可以增加key_mutex字段,說明獲得鎖 //業務邏輯操作 value = db.get(key); memcache.set(key, value); //刪除key_mutex memcache.delete(key_mutex); } else { //從進程執行流 sleep(50); retry(); } }
參考
http://www.cnblogs.com/dluf/p/3849075.html
http://timyang.net/programming/memcache-mutex/
Linux多線程服務端編程. Page 356~360.
從Paxos到ZooKeeper——分布式一致性原理與實踐
來自:http://fuzhii.com/2015/09/21/concurrentlock/