<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    jinfeng_wang

    G-G-S,D-D-U!

    BlogJava 首頁 新隨筆 聯系 聚合 管理
      400 Posts :: 0 Stories :: 296 Comments :: 0 Trackbacks

    轉載請注明出處哈:http://carlosfu.iteye.com/blog/2269678


     

      一. 緩存穿透 (請求數據緩存大量不命中):
        緩存穿透是指查詢一個一定不存在的數據,由于緩存不命中,并且出于容錯考慮, 如果從存儲層查不到數據則不寫入緩存,這將導致這個不存在的數據每次請求都要到存儲層去查詢,失去了緩存的意義。
        例如:下圖是一個比較典型的cache-storage架構,cache(例如memcache, redis等等) + storage(例如mysql, hbase等等)架構,查一個壓根就不存在的值, 如果不做兼容,永遠會查詢storage。
    二. 危害:
         對底層數據源(mysql, hbase, http接口, rpc調用等等)壓力過大,有些底層數據源不具備高并發性。
         例如mysql一般來說單臺能夠扛1000-QPS就已經很不錯了(別說你的查詢都是select * from table where id=xx 以及你的機器多么牛逼,那就有點矯情了)
         例如他人提供的一個抗壓性很差的http接口,可能穿透會擊潰他的服務。
         
    三. 如何發現:
       我們可以分別記錄cache命中數, storage命中數,以及總調用量,如果發現空命中(cache,storage都沒有命中)較多,可能就會在緩存穿透問題。
       注意:緩存本身的命中率(例如redis中的info提供了類似數字,只代表緩存本身)不代表storage和業務的命中率。
       
    四. 產生原因以及業務是否允許?
        產生原因有很多:可能是代碼本身或者數據存在的問題造成的,也很有可能是一些惡意攻擊、爬蟲等等(因為http讀接口都是開放的)
        業務是否允許:這個要看做的項目或者業務是否允許這種情況發生,比如做一些非實時的推薦系統,假如新用戶來了,確實沒有他的推薦數據(推薦數據通常是根據歷史行為算出),這種業務是會發生穿透現象的,至于業務允不允許要具體問題具體分析了。
     
    五. 解決方法:
    解決思路大致有兩個,如下表。下面將分別說明
    解決緩存穿透適用場景維護成本
    緩存空對象

    1. 數據命中不高

    2. 數據頻繁變化實時性高

    1.代碼維護簡單

    2.需要過多的緩存空間

    3. 數據不一致

    bloomfilter或者壓縮filter提前攔截

    1. 數據命中不高

    2. 數據相對固定實時性低

    1.代碼維護復雜

    2.緩存空間占用少

           1. 緩存空對象
             
            (1). 定義:如上圖所示,當第②步MISS后,仍然將空對象保留到Cache中(可能是保留幾分鐘或者一段時間,具體問題具體分析),下次新的Request(同一個key)將會從Cache中獲取到數據,保護了后端的Storage。
            (2) 適用場景:數據命中不高,數據頻繁變化實時性高(一些亂轉業務)
            (3) 維護成本:代碼比較簡單,但是有兩個問題:
                 第一是空值做了緩存,意味著緩存系統中存了更多的key-value,也就是需要更多空間(有人說空值沒多少,但是架不住多啊),解決方法是我們可以設置一個較短的過期時間。
                 第二是數據會有一段時間窗口的不一致,假如,Cache設置了5分鐘過期,此時Storage確實有了這個數據的值,那此段時間就會出現數據不一致,解決方法是我們可以利用消息或者其他方式,清除掉Cache中的數據。
            (4) 偽代碼:
    Java代碼  收藏代碼
    1. package com.carlosfu.service;  
    2.   
    3. import org.apache.commons.lang.StringUtils;  
    4.   
    5. import com.carlosfu.cache.Cache;  
    6. import com.carlosfu.storage.Storage;  
    7.   
    8. /** 
    9.  * 某服務 
    10.  *  
    11.  * @author carlosfu 
    12.  * @Date 2015-10-11 
    13.  * @Time 下午6:28:46 
    14.  */  
    15. public class XXXService {  
    16.   
    17.     /** 
    18.      * 緩存 
    19.      */  
    20.     private Cache cache = new Cache();  
    21.   
    22.     /** 
    23.      * 存儲 
    24.      */  
    25.     private Storage storage = new Storage();  
    26.   
    27.     /** 
    28.      * 模擬正常模式 
    29.      * @param key 
    30.      * @return 
    31.      */  
    32.     public String getNormal(String key) {  
    33.         // 從緩存中獲取數據  
    34.         String cacheValue = cache.get(key);  
    35.         // 緩存為空  
    36.         if (StringUtils.isBlank(cacheValue)) {  
    37.             // 從存儲中獲取  
    38.             String storageValue = storage.get(key);  
    39.             // 如果存儲數據不為空,將存儲的值設置到緩存  
    40.             if (StringUtils.isNotBlank(storageValue)) {  
    41.                 cache.set(key, storageValue);  
    42.             }  
    43.             return storageValue;  
    44.         } else {  
    45.             // 緩存非空  
    46.             return cacheValue;  
    47.         }  
    48.     }  
    49.   
    50.   
    51.     /** 
    52.      * 模擬防穿透模式 
    53.      * @param key 
    54.      * @return 
    55.      */  
    56.     public String getPassThrough(String key) {  
    57.         // 從緩存中獲取數據  
    58.         String cacheValue = cache.get(key);  
    59.         // 緩存為空  
    60.         if (StringUtils.isBlank(cacheValue)) {  
    61.             // 從存儲中獲取  
    62.             String storageValue = storage.get(key);  
    63.             cache.set(key, storageValue);  
    64.             // 如果存儲數據為空,需要設置一個過期時間(300秒)  
    65.             if (StringUtils.isBlank(storageValue)) {  
    66.                 cache.expire(key, 60 * 5);  
    67.             }  
    68.             return storageValue;  
    69.         } else {  
    70.             // 緩存非空  
    71.             return cacheValue;  
    72.         }  
    73.     }  
    74.   
    75. }  
     
    2. bloomfilter或者壓縮filter(bitmap等等)提前攔截
            (1). 定義:如上圖所示,在訪問所有資源(cache, storage)之前,將存在的key用布隆過濾器提前保存起來,做第一層攔截, 例如: 我們的推薦服務有4億個用戶uid, 我們會根據用戶的歷史行為進行推薦(非實時),所有的用戶推薦數據放到hbase中,但是每天有許多新用戶來到網站,這些用戶在當天的訪問就會穿透到hbase。為此我們每天4點對所有uid做一份布隆過濾器。如果布隆過濾器認為uid不存在,那么就不會訪問hbase,在一定程度保護了hbase(減少30%左右)。
                  注:有關布隆過濾器的相關知識,請自行查閱,有關guava中如何使用布隆過濾器,之后會系列文章給大家介紹。
            (2) 適用場景:數據命中不高,數據相對固定實時性低(通常是數據集較大)
            (3) 維護成本:代碼維護復雜, 緩存空間占用少
                  第一是空值做了緩存,意味著緩存系統中存了更多的key-value,也就是需要更多空間(有人說空值沒多少,但是架不住多啊),解決方法是我們可以設置一個較短的過期時間。
                  第二是數據會有一段時間窗口的不一致,假如,Cache設置了5分鐘過期,此時Storage確實有了這個數據的值,那此段時間就會出現數據不一致,解決方法是我們可以利用消息或者其他方式,清除掉Cache中的數據。
    六、參考資料:
    http://blog.jobbole.com/83439/ (那些年我們一起追過的緩存寫法)
    附圖一張,單機負載,哈哈:
      
     
    posted on 2016-12-20 17:14 jinfeng_wang 閱讀(205) 評論(0)  編輯  收藏 所屬分類: 2016-REDIS
    主站蜘蛛池模板: 免费成人激情视频| 又大又硬又爽又粗又快的视频免费| 成年女人毛片免费播放视频m| 亚洲视频免费在线播放| 最近2019中文免费字幕在线观看| 国产亚洲欧洲Aⅴ综合一区| 久久国产福利免费| 亚洲精品国产精品乱码在线观看 | 久久精品亚洲中文字幕无码网站| 中文字幕久无码免费久久| 亚洲自偷自偷偷色无码中文| 国产线视频精品免费观看视频| 久久久久久久综合日本亚洲| 波多野结衣免费一区视频| 亚洲伦理一区二区| 日本亚洲免费无线码| 亚洲人成电影网站色www| 免费看国产精品麻豆| 中国一级特黄的片子免费 | 亚洲日韩中文字幕无码一区| 日韩免费高清视频网站| 一级特黄色毛片免费看| 亚洲精品白浆高清久久久久久 | 免费无码又爽又刺激高潮软件| 亚洲av综合色区| 免费av欧美国产在钱| 特级毛片aaaa免费观看| 亚洲AV日韩AV永久无码久久| 午夜不卡久久精品无码免费 | 免费看一级一级人妻片| 亚洲AV无码一区东京热| 免费观看成人毛片a片2008| 亚洲av无码成人精品区一本二本| 亚洲色欲久久久久综合网| 无码人妻久久一区二区三区免费| 亚洲午夜一区二区三区| 久久久久亚洲AV成人网人人网站| 亚洲精品视频在线免费| 日韩a毛片免费观看| 亚洲酒色1314狠狠做| 亚洲?V无码乱码国产精品|