週六的S2 Web 2.0技術沙龍上介紹了memcache中使用mutex場景(文後要演講稿),有網友對詳情感興趣,簡單介紹以下。php
Mutex主要用於有大量併發訪問並存在cache過時的場合,如html
在大併發的場合,當cache失效時,大量併發同時取不到cache,會同一瞬間去訪問db並回設cache,可能會給系統帶來潛在的超負荷風險。咱們曾經在線上系統出現過相似故障。數據庫
方法一
在load db以前先add一個mutex key, mutex key add成功以後再去作加載db, 若是add失敗則sleep以後重試讀取原cache數據。爲了防止死鎖,mutex key也須要設置過時時間。僞代碼以下
(注:下文僞代碼僅供瞭解思路,可能存在bug,歡迎隨時指出。)緩存
if (memcache.get(key) == null) { // 3 min timeout to avoid mutex holder crash if (memcache.add(key_mutex, 3 * 60 * 1000) == true) { value = db.get(key); memcache.set(key, value); memcache.delete(key_mutex); } else { sleep(50); retry(); } }
方法二
在value內部設置1個超時值(timeout1), timeout1比實際的memcache timeout(timeout2)小。當從cache讀取到timeout1發現它已通過期時候,立刻延長timeout1並從新設置到cache。而後再從數據庫加載數據並設置到cache中。僞代碼以下併發
v = memcache.get(key); if (v == null) { if (memcache.add(key_mutex, 3 * 60 * 1000) == true) { value = db.get(key); memcache.set(key, value); memcache.delete(key_mutex); } else { sleep(50); retry(); } } else { if (v.timeout <= now()) { if (memcache.add(key_mutex, 3 * 60 * 1000) == true) { // extend the timeout for other threads v.timeout += 3 * 60 * 1000; memcache.set(key, v, KEY_TIMEOUT * 2); // load the latest value from db v = db.get(key); v.timeout = KEY_TIMEOUT; memcache.set(key, value, KEY_TIMEOUT * 2); memcache.delete(key_mutex); } else { sleep(50); retry(); } } }
相對於方案一
優勢:避免cache失效時刻大量請求獲取不到mutex並進行sleep
缺點:代碼複雜性增大,所以通常場合用方案一也已經足夠。app
方案二在Memcached FAQ中也有詳細介紹 How to prevent clobbering updates, stampeding requests,而且Brad還介紹了用他另一個得意的工具 Gearman 來實現單實例設置cache的方法,見 Cache miss stampedes,不過用Gearman來解決就感受就有點奇技淫巧了。less
附:本次Web2.0技術沙龍演講主題:微博Cache設計談,需下載請點擊演講稿下menu/download (需登陸slideshare)。ide