1. 程式人生 > >memcached快取失效時的高併發訪問問題解決

memcached快取失效時的高併發訪問問題解決

memcached一般用於在訪問一些效能相對低下的資料介面時(如資料庫),為了保證這些資料介面的穩定性,加上memcached以減少訪問次數,保證這些資料介面的健壯性。一般memcached的資料都是定時失效的,當資料失效時一般會再次去訪問取資料介面,然後將其更新至memcached中。這時就會有一個問題,當某個資料失效時,恰好同時有大量的客戶端訪問該資料,這時這些客戶端都會發現該資料失效,然後都會去呼叫資料介面去取資料更新,這自然就瞬間地使資料介面失去了memcached的保護,有可能造成系統的故障。

        那麼如何解決這個問題呢?

        第一種:資料不失效,定時更新。即資料存放在memcached中永不失效,但是會有一個定時任務,定時的去更新這個資料。

        第二種:既然該問題的癥結在於在資料失效時,會有多個客戶端去呼叫資料介面,那麼只要想辦法在資料失效時只有一個客戶端能訪問資料介面即可,要做到這點,自然的想法是加鎖:如下:

[java] view plaincopyprint?
  1. object value = memcached.get(key);  
  2. if(null==value){  
  3.         synchronized{  
  4.                value = memcached.get(key);  
  5.                if(null==value){  
  6.                       value = db.get(key);  
  7.                       memcached.set(key,value);  
  8.                 }  
  9.         }  
  10. }  
  11. return value;  

        這樣做的前提是你必須保證這個函式的類是單例的,顯然在伺服器叢集中不可能有這樣的場景,那麼如果在群集間加鎖呢?解鈴還需繫鈴人,既然大家共用一個memcached伺服器,那麼就使用memcached來實現這個鎖機制。即當客戶端取不到資料時,先在memcached中設定一個flag表明當前客戶端在更新該資料,當其它客戶端也來訪問時發現失效後就等待直到更新好資料為目。

[java]
 view plaincopyprint?
  1. object value = memcahced.get(key);  
  2. if(null=value){  
  3.     if(memcached.add(key)){  
  4.         value = db.get();  
  5.         memcached.set(key,value);  
  6.     }else{  
  7.         while(true){  
  8.             Thread.sleep(50);  
  9.              value=memcached.get(key);  
  10.              if(null!=value){  
  11.                   break;  
  12.              }  
  13.         }  
  14.     }  
  15. }  
  16. return value;  
         memcached中的add方法是實現該功能的關鍵,該方法是判斷memcached中是否有某個key存在,如果存在則返回false,否則返回true,並新增該key值,如果沒有該方法,顯然我們只能再一次的通過get 和set去設定該值,顯然這樣做是執行緒不安全的,有可能有多個客戶端同時取為空,同時去取資料並更新。

          上述的方法存在的缺陷時,一旦資料失效,所有客戶端要等待某個客戶端更新完畢,這樣勢必增加伺服器壓力,可以通過在key失效之間的一段時間就觸發更新的方式來解決這個問題。