在大並發的場合,當cache失效時,大量並發同時取不到cache,會同一瞬間去訪問db並回設cache,可能會給系統帶來潛在的超負荷風險。
解決方法
方法一
在load db之前先add一個mutex key, mutex key add成功之后再去做加載db, 如果add失敗則sleep之后重試讀取原cache數據。為了防止死鎖,mutex key也需要設置過期時間。偽代碼如下
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(); } } }