91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

緩存熱點key問題(mutex key)

發布時間:2020-09-14 12:17:54 來源:網絡 閱讀:640 作者:rr57e735816465a 欄目:開發技術

一、引出熱點key問題

 

       我們通常使用 緩存 + 過期時間的策略來幫助我們加速接口的訪問速度,減少了后端負載,同時保證功能的更新,一般情況下這種模式已經基本滿足要求了。

       但是有兩個問題如果同時出現,可能就會對系統造成致命的危害:

      (1) 這個key是一個熱點key(例如一個重要的新聞,一個熱門的八卦新聞等等),所以這種key訪問量可能非常大。

      (2) 緩存的構建是需要一定時間的。(可能是一個復雜計算,例如復雜的sql、多次IO、多個依賴(各種接口)等等

 

       于是就會出現一個致命問題:在緩存失效的瞬間,有大量線程來構建緩存(見下圖),造成后端負載加大,甚至可能會讓系統崩潰 。

 緩存熱點key問題(mutex key)

    

         

 

 

二、四種解決方案

 

我們的目標是:盡量少的線程構建緩存(甚至是一個) + 數據一致性 + 較少的潛在危險,下面會介紹四種方法來解決這個問題:

 

1. 使用互斥鎖(mutex key): 這種解決方案思路比較簡單,就是只讓一個線程構建緩存,其他線程等待構建緩存的線程執行完,重新從緩存獲取數據就可以了(如下圖)下載   

緩存熱點key問題(mutex key)

     如果是單機,可以用synchronized或者lock來處理,如果是分布式環境可以用分布式鎖就可以了(分布式鎖,可以用memcache的add, redis的setnx, zookeeper的添加節點操作)。

     下面是Tim yang博客的代碼,是memcache的偽代碼實現下載   

      

Java代碼  

  1. if (memcache.get(key) == null) {  

  2.     // 3 min timeout to avoid mutex holder crash  

  3.     if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {  

  4.         value = db.get(key);  

  5.         memcache.set(key, value);  

  6.         memcache.delete(key_mutex);  

  7.     } else {  

  8.         sleep(50);  

  9.         retry();  

  10.     }  

  11. }  

     

 

      如果換成redis,就是:

Java代碼  下載   

  1. String get(String key) {  

  2.    String value = redis.get(key);  

  3.    if (value  == null) {  

  4.     if (redis.setnx(key_mutex, "1")) {  

  5.         // 3 min timeout to avoid mutex holder crash  

  6.         redis.expire(key_mutex, 3 * 60)  

  7.         value = db.get(key);  

  8.         redis.set(key, value);  

  9.         redis.delete(key_mutex);  

  10.     } else {  

  11.         //其他線程休息50毫秒后重試  

  12.         Thread.sleep(50);  

  13.         get(key);  

  14.     }  

  15.   }  

  16. }  

 

 

       

2. "提前"使用互斥鎖(mutex key):

   在value內部設置1個超時值(timeout1), timeout1比實際的memcache timeout(timeout2)小。當從cache讀取到timeout1發現它已經過期時候,馬上延長timeout1并重新設置到cache。然后再從數據庫加載數據并設置到cache中。偽代碼如下:

 

Java代碼  下載   

  1. v = memcache.get(key);  

  2. if (v == null) {  

  3.     if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {  

  4.         value = db.get(key);  

  5.         memcache.set(key, value);  

  6.         memcache.delete(key_mutex);  

  7.     } else {  

  8.         sleep(50);  

  9.         retry();  

  10.     }  

  11. else {  

  12.     if (v.timeout <= now()) {  

  13.         if (memcache.add(key_mutex, 3 * 60 * 1000) == true) {  

  14.             // extend the timeout for other threads  

  15.             v.timeout += 3 * 60 * 1000;  

  16.             memcache.set(key, v, KEY_TIMEOUT * 2);  

  17.   

  18.             // load the latest value from db  

  19.             v = db.get(key);  

  20.             v.timeout = KEY_TIMEOUT;  

  21.             memcache.set(key, value, KEY_TIMEOUT * 2);  

  22.             memcache.delete(key_mutex);  

  23.         } else {  

  24.             sleep(50);  

  25.             retry();  

  26.         }  

  27.     }  

  28. }  

 

 

 

3. "永遠不過期"

    

    這里的“永遠不過期”包含兩層意思:

    (1) 從redis上看,確實沒有設置過期時間,這就保證了,不會出現熱點key過期問題,也就是“物理”不過期。

    (2) 從功能上看,如果不過期,那不就成靜態的了嗎?所以我們把過期時間存在key對應的value里,如果發現要過期了,通過一個后臺的異步線程進行緩存的構建,也就是“邏輯”過期下載   

   緩存熱點key問題(mutex key)

    從實戰看,這種方法對于性能非常友好,唯一不足的就是構建緩存時候,其余線程(非構建緩存的線程)可能訪問的是老數據,但是對于一般的互聯網功能來說這個還是可以忍受。

   

Java代碼  下載   

  1. String get(final String key) {  

  2.         V v = redis.get(key);  

  3.         String value = v.getValue();  

  4.         long timeout = v.getTimeout();  

  5.         if (v.timeout <= System.currentTimeMillis()) {  

  6.             // 異步更新后臺異常執行  

  7.             threadPool.execute(new Runnable() {  

  8.                 public void run() {  

  9.                     String keyMutex = "mutex:" + key;  

  10.                     if (redis.setnx(keyMutex, "1")) {  

  11.                         // 3 min timeout to avoid mutex holder crash  

  12.                         redis.expire(keyMutex, 3 * 60);  

  13.                         String dbValue = db.get(key);  

  14.                         redis.set(key, dbValue);  

  15.                         redis.delete(keyMutex);  

  16.                     }  

  17.                 }  

  18.             });  

  19.         }  

  20.         return value;  

  21.     }  

 

 

 

4. 資源保護

       可以做資源的隔離保護主線程池,如果把這個應用到緩存的構建也未嘗不可。下載   

緩存熱點key問題(mutex key)

 

 

 

三、四種方案對比:

 

      作為一個并發量較大的互聯網應用,我們的目標有3個:

      1. 加快用戶訪問速度,提高用戶體驗。

      2. 降低后端負載,保證系統平穩。下載   

      3. 保證數據“盡可能”及時更新(要不要完全一致,取決于業務,而不是技術。)

      所以第二節中提到的四種方法,可以做如下比較,還是那就話:沒有最好,只有最合適。 

解決方案優點缺點
簡單分布式鎖(Tim yang)

 1. 思路簡單

2. 保證一致性

1. 代碼復雜度增大

2. 存在死鎖的風險

3. 存在線程池阻塞的風險

加另外一個過期時間(Tim yang) 1. 保證一致性同上 
不過期(本文)

1. 異步構建緩存,不會阻塞線程池

1. 不保證一致性。

2. 代碼復雜度增大(每個value都要維護一個timekey)。

3. 占用一定的內存空間(每個value都要維護一個timekey)。

資源隔離組件hystrix(本文)

1. hystrix技術成熟,有效保證后端。

2. hystrix監控強大。

 

 

1. 部分訪問存在降級策略。

 

 

四、總結

 

   1.  熱點key + 過期時間 + 復雜的構建緩存過程 => mutex key問題

   2. 構建緩存一個線程做就可以了。

   3. 四種解決方案:沒有最佳只有最合適。


向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

双辽市| 浮梁县| 杭州市| 荣成市| 鸡东县| 西华县| 华安县| 静乐县| 临高县| 若尔盖县| 梁平县| 鄄城县| 临城县| 尼玛县| 花垣县| 咸阳市| 鄂托克前旗| 禄劝| 兴化市| 綦江县| 屯留县| 邓州市| 渑池县| 揭阳市| 城固县| 玛纳斯县| 甘孜| 天柱县| 来宾市| 沙洋县| 丘北县| 桂林市| 桦南县| 互助| 仁布县| 山阳县| 桃源县| 伊通| 蕲春县| 比如县| 扎鲁特旗|