gmnon.cn-疯狂蹂躏欧美一区二区精品,欧美精品久久久久a,高清在线视频日韩欧美,日韩免费av一区二区

站長資訊網(wǎng)
最全最豐富的資訊網(wǎng)站

高并發(fā)技巧之Redis和本地緩存使用技巧分享

本篇文章給大家?guī)砹岁P(guān)于Redis的相關(guān)知識,其中主要介紹的是分布式緩存和本地緩存的使用技巧,包括緩存種類介紹,各種的使用場景,以及如何使用,最后再給出實戰(zhàn)案例,下面一起來看一下,希望對大家有幫助。

高并發(fā)技巧之Redis和本地緩存使用技巧分享

千萬級數(shù)據(jù)并發(fā)如何處理?進入學(xué)習(xí)

推薦學(xué)習(xí):Redis視頻教程

眾所周知,緩存最主要的目的就是加速訪問,緩解數(shù)據(jù)庫壓力。最常用的緩存就是分布式緩存,比如redis,在面對大部分并發(fā)場景或者一些中小型公司流量沒有那么高的情況,使用redis基本都能解決了。但是在流量較高的情況下可能得使用到本地緩存了,比如guava的LoadingCache和快手開源的ReloadableCache。

三種緩存的使用場景

這部分會介紹redis,比如guava的LoadingCache和快手開源的ReloadableCache的使用場景和局限,通過這一部分的介紹就能知道在怎樣的業(yè)務(wù)場景下應(yīng)該使用哪種緩存,以及為什么。

Redis的使用場景和局限性

如果寬泛的說redis何時使用,那么自然就是用戶訪問量過高的地方使用,從而加速訪問,并且緩解數(shù)據(jù)庫壓力。如果細分的話,還得分為單節(jié)點問題和非單節(jié)點問題。

如果一個頁面用戶訪問量比較高,但是訪問的不是同一個資源。比如用戶詳情頁,訪問量比較高,但是每個用戶的數(shù)據(jù)都是不一樣的,這種情況顯然只能用分布式緩存了,如果使用redis,key為用戶唯一鍵,value則是用戶信息。

redis導(dǎo)致的緩存擊穿。

但是需要注意一點,一定要設(shè)置過期時間,而且不能設(shè)置到同一時間點過期。舉個例子,比如用戶又個活動頁,活動頁能看到用戶活動期間獲獎數(shù)據(jù),粗心的人可能會設(shè)置用戶數(shù)據(jù)的過期時間點為活動結(jié)束,這樣會

單(熱)點問題

單節(jié)點問題說的是redis的單個節(jié)點的并發(fā)問題,因為對于相同的key會落到redis集群的同一個節(jié)點上,那么如果對這個key的訪問量過高,那么這個redis節(jié)點就存在并發(fā)隱患,這個key就稱為熱key。

如果所有用戶訪問的都是同一個資源,比如小愛同學(xué)app首頁對所有用戶展示的內(nèi)容都一樣(初期),服務(wù)端給h5返回的是同一個大json,顯然得使用到緩存。首先我們考慮下用redis是否可行,由于redis存在單點問題,如果流量過大的話,那么所有用戶的請求到達redis的同一個節(jié)點,需要評估該節(jié)點能否抗住這么大流量。我們的規(guī)則是,如果單節(jié)點qps達到了千級別就要解決單點問題了(即使redis號稱能抗住十萬級別的qps),最常見的做法就是使用本地緩存。顯然小愛app首頁流量不過百,使用redis是沒問題的。

LoadingCache的使用場景和局限性

對于這上面說的熱key問題,我們最直接的做法就是使用本地緩存,比如你最熟悉的guava的LoadingCache,但是使用本地緩存要求能夠接受一定的臟數(shù)據(jù),因為如果你更新了首頁,本地緩存是不會更新的,它只會根據(jù)一定的過期策略來重新加載緩存,不過在我們這個場景是完全沒問題的,因為一旦在后臺推送了首頁后就不會再去改變了。即使改變了也沒問題,可以設(shè)置寫過期為半小時,超過半小時重新加載緩存,這種短時間內(nèi)的臟數(shù)據(jù)我們是可以接受的。

LoadingCache導(dǎo)致的緩存擊穿

雖然說本地緩存和機器上強相關(guān)的,雖然代碼層面寫的是半小時過期,但由于每臺機器的啟動時間不同,導(dǎo)致緩存的加載時間不同,過期時間也就不同,也就不會所有機器上的請求在同一時間緩存失效后都去請求數(shù)據(jù)庫。但是對于單一一臺機器也是會導(dǎo)致緩存穿透的,假如有10臺機器,每臺1000的qps,只要有一臺緩存過期就可能導(dǎo)致這1000個請求同時打到了數(shù)據(jù)庫。這種問題其實比較好解決,但是容易被忽略,也就是在設(shè)置LoadingCache的時候使用LoadingCache的load-miss方法,而不是直接判斷cache.getIfPresent()== null然后去請求db;前者會加虛擬機層面的鎖,保證只有一個請求打到數(shù)據(jù)庫去,從而完美的解決了這個問題。

但是,如果對于實時性要求較高的情況,比如有段時間要經(jīng)常做活動,我要保證活動頁面能近實時更新,也就是運營在后臺配置好了活動信息后,需要在C端近實時展示這次配置的活動信息,此時使用LoadingCache肯定就不能滿足了。

ReloadableCache的使用場景和局限性

對于上面說的LoadingCache不能解決的實時問題,可以考慮使用ReloadableCache,這是快手開源的一個本地緩存框架,最大的特點是支持多機器同時更新緩存,假設(shè)我們修改了首頁信息,然后請求打到的是A機器,這個時候重新加載ReloadableCache,然后它會發(fā)出通知,監(jiān)聽了同一zk節(jié)點的其他機器收到通知后重新更新緩存。使用這個緩存一般的要求是將全量數(shù)據(jù)加載到本地緩存,所以如果數(shù)據(jù)量過大肯定會對gc造成壓力,這種情況就不能使用了。由于小愛同學(xué)首頁這個首頁是帶有狀態(tài)的,一般online狀態(tài)的就那么兩個,所以完全可以使用ReloadableCache來只裝載online狀態(tài)的首頁。

小結(jié)

到這里三種緩存基本都介紹完了,做個小結(jié):

  • 對于非熱點的數(shù)據(jù)訪問,比如用戶維度的數(shù)據(jù),直接使用redis即可;
  • 對于熱點數(shù)據(jù)的訪問,如果流量不是很高,無腦使用redis即可;
  • 對于熱點數(shù)據(jù),如果允許一定時間內(nèi)的臟數(shù)據(jù),使用LoadingCache即可;
  • 對于熱點數(shù)據(jù),如果一致性要求較高,同時數(shù)據(jù)量不大的情況,使用ReloadableCache即可;

小技巧

不管哪種本地緩存雖然都帶有虛擬機層面的加鎖來解決擊穿問題,但是意外總有可能以你意想不到的方式發(fā)生,保險起見你可以使用兩級緩存的方式即本地緩存+redis+db。

緩存使用的簡單介紹

這里redis的使用就不再多說了,相信很多人對api的使用比我還熟悉

LoadingCache的使用

這個是guava提供的網(wǎng)上一抓一大把,但是給兩點注意事項

  • 要使用load-miss的話, 要么使用V get(K key, Callable<? extends V> loader);要么使用build的時候使用的是build(CacheLoader<? super K1, V1> loader)這個時候可以直接使用get()了。此外建議使用load-miss,而不是getIfPresent==null的時候再去查數(shù)據(jù)庫,這可能導(dǎo)致緩存擊穿;
  • 使用load-miss是因為這是線程安全的,如果緩存失效的話,多個線程調(diào)用get的時候只會有一個線程去db查詢,其他線程需要等待,也就是說這是線程安全的。

LoadingCache<String, String> cache = CacheBuilder.newBuilder()                 .maximumSize(1000L)                 .expireAfterAccess(Duration.ofHours(1L)) // 多久不訪問就過期                 .expireAfterWrite(Duration.ofHours(1L))  // 多久這個key沒修改就過期                 .build(new CacheLoader<String, String>() {                     @Override                     public String load(String key) throws Exception {                         // 數(shù)據(jù)裝載方式,一般就是loadDB                         return key + " world";                     }                 }); String value = cache.get("hello"); // 返回hello world
登錄后復(fù)制

reloadableCache的使用

導(dǎo)入三方依賴

<dependency>   <groupId>com.github.phantomthief</groupId>   <artifactId>zknotify-cache</artifactId>   <version>0.1.22</version> </dependency>
登錄后復(fù)制

需要看文檔,不然無法使用,有興趣自己寫一個也行的。

public interface ReloadableCache<T> extends Supplier<T> {      /**      * 獲取緩存數(shù)據(jù)      */     @Override     T get();      /**      * 通知全局緩存更新      * 注意:如果本地緩存沒有初始化,本方法并不會初始化本地緩存并重新加載      *      * 如果需要初始化本地緩存,請先調(diào)用 {@link ReloadableCache#get()}      */     void reload();      /**      * 更新本地緩存的本地副本      * 注意:如果本地緩存沒有初始化,本方法并不會初始化并刷新本地的緩存      *      * 如果需要初始化本地緩存,請先調(diào)用 {@link ReloadableCache#get()}      */     void reloadLocal(); }
登錄后復(fù)制

老生常談的緩存擊穿/穿透/雪崩問題

這三個真的是亙古不變的問題,如果流量大確實需要考慮。

緩存擊穿

簡單說就是緩存失效,導(dǎo)致大量請求同一時間打到了數(shù)據(jù)庫。對于緩存擊穿問題上面已經(jīng)給出了很多解決方案了。

  • 比如使用本地緩存
  • 本地緩存使用load-miss方法
  • 使用第三方服務(wù)來加載緩存

1.2和都說過,主要來看3。假如業(yè)務(wù)愿意只能使用redis而無法使用本地緩存,比如數(shù)據(jù)量過大,實時性要求比較高。那么當(dāng)緩存失效的時候就得想辦法保證只有少量的請求打到數(shù)據(jù)庫。很自然的就想到了使用分布式鎖,理論上說是可行的,但實際上存在隱患。我們的分布式鎖相信很多人都是使用redis+lua的方式實現(xiàn)的,并且在while中進行了輪訓(xùn),這樣請求量大,數(shù)據(jù)多的話會導(dǎo)致無形中讓redis成了隱患,并且占了太多業(yè)務(wù)線程,其實僅僅是引入了分布式鎖就加大了復(fù)雜度,我們的原則就是能不用就不用。

那么我們是不是可以設(shè)計一個類似分布式鎖,但是更可靠的rpc服務(wù)呢?當(dāng)調(diào)用get方法的時候這個rpc服務(wù)保證相同的key打到同一個節(jié)點,并且使用synchronized來進行加鎖,之后完成數(shù)據(jù)的加載。在快手提供了一個叫cacheSetter的框架。下面提供一個簡易版,自己寫也很容易實現(xiàn)。

import com.google.common.collect.Lists; import org.apache.commons.collections4.CollectionUtils;  import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CountDownLatch;  /**  * @Description 分布式加載緩存的rpc服務(wù),如果部署了多臺機器那么調(diào)用端最好使用id做一致性hash保證相同id的請求打到同一臺機器。  **/ public abstract class AbstractCacheSetterService implements CacheSetterService {      private final ConcurrentMap<String, CountDownLatch> loadCache = new ConcurrentHashMap<>();      private final Object lock = new Object();      @Override     public void load(Collection<String> needLoadIds) {         if (CollectionUtils.isEmpty(needLoadIds)) {             return;         }         CountDownLatch latch;         Collection<CountDownLatch> loadingLatchList;         synchronized (lock) {             loadingLatchList = excludeLoadingIds(needLoadIds);              needLoadIds = Collections.unmodifiableCollection(needLoadIds);              latch = saveLatch(needLoadIds);         }         System.out.println("needLoadIds:" + needLoadIds);         try {             if (CollectionUtils.isNotEmpty(needLoadIds)) {                 loadCache(needLoadIds);             }         } finally {             release(needLoadIds, latch);             block(loadingLatchList);         }      }      /**      * 加鎖      * @param loadingLatchList 需要加鎖的id對應(yīng)的CountDownLatch      */     protected void block(Collection<CountDownLatch> loadingLatchList) {         if (CollectionUtils.isEmpty(loadingLatchList)) {             return;         }         System.out.println("block:" + loadingLatchList);         loadingLatchList.forEach(l -> {             try {                 l.await();             } catch (InterruptedException e) {                 e.printStackTrace();             }         });     }      /**      * 釋放鎖      * @param needLoadIds 需要釋放鎖的id集合      * @param latch 通過該CountDownLatch來釋放鎖      */     private void release(Collection<String> needLoadIds, CountDownLatch latch) {         if (CollectionUtils.isEmpty(needLoadIds)) {             return;         }         synchronized (lock) {             needLoadIds.forEach(id -> loadCache.remove(id));         }         if (latch != null) {             latch.countDown();         }     }      /**      * 加載緩存,比如根據(jù)id從db查詢數(shù)據(jù),然后設(shè)置到redis中      * @param needLoadIds 加載緩存的id集合      */     protected abstract void loadCache(Collection<String> needLoadIds);      /**      * 對需要加載緩存的id綁定CountDownLatch,后續(xù)相同的id請求來了從map中找到CountDownLatch,并且await,直到該線程加載完了緩存      * @param needLoadIds 能夠正在去加載緩存的id集合      * @return 公用的CountDownLatch      */     protected CountDownLatch saveLatch(Collection<String> needLoadIds) {         if (CollectionUtils.isEmpty(needLoadIds)) {             return null;         }         CountDownLatch latch = new CountDownLatch(1);         needLoadIds.forEach(loadId -> loadCache.put(loadId, latch));         System.out.println("loadCache:" + loadCache);         return latch;     }      /**      * 哪些id正在加載數(shù)據(jù),此時持有相同id的線程需要等待      * @param ids 需要加載緩存的id集合      * @return 正在加載的id所對應(yīng)的CountDownLatch集合      */     private Collection<CountDownLatch> excludeLoadingIds(Collection<String> ids) {         List<CountDownLatch> loadingLatchList = Lists.newArrayList();         Iterator<String> iterator = ids.iterator();         while (iterator.hasNext()) {             String id = iterator.next();             CountDownLatch latch = loadCache.get(id);             if (latch != null) {                 loadingLatchList.add(latch);                 iterator.remove();             }         }         System.out.println("loadingLatchList:" + loadingLatchList);         return loadingLatchList;     } }
登錄后復(fù)制

業(yè)務(wù)實現(xiàn)

import java.util.Collection; public class BizCacheSetterRpcService extends AbstractCacheSetterService {     @Override     protected void loadCache(Collection<String> needLoadIds) {         // 讀取db進行處理    	// 設(shè)置緩存     } }
登錄后復(fù)制

緩存穿透

簡單來說就是請求的數(shù)據(jù)在數(shù)據(jù)庫不存在,導(dǎo)致無效請求打穿數(shù)據(jù)庫。

解法也很簡單,從db獲取數(shù)據(jù)的方法(getByKey(K key))一定要給個默認值。

比如我有個獎池,金額上限是1W,用戶完成任務(wù)的時候給他發(fā)筆錢,并且使用redis記錄下來,并且落表,用戶在任務(wù)頁面能實時看到獎池剩余金額,在任務(wù)開始的時候顯然獎池金額是不變的,redis和db里面都沒有發(fā)放金額的記錄,這就導(dǎo)致每次必然都去查db,對于這種情況,從db沒查出來數(shù)據(jù)應(yīng)該緩存?zhèn)€值0到緩存。

緩存雪崩

就是大量緩存集中失效打到了db,當(dāng)然肯定都是一類的業(yè)務(wù)緩存,歸根到底是代碼寫的有問題??梢詫⒕彺媸У倪^期時間打散,別讓其集中失效就可以了。

推薦學(xué)習(xí):Redis視頻教程

贊(0)
分享到: 更多 (0)
網(wǎng)站地圖   滬ICP備18035694號-2    滬公網(wǎng)安備31011702889846號
gmnon.cn-疯狂蹂躏欧美一区二区精品,欧美精品久久久久a,高清在线视频日韩欧美,日韩免费av一区二区
天天干天天爽天天射| 国产精品999.| 少妇高潮喷水久久久久久久久久| 亚洲黄色网址在线观看| 成人污网站在线观看| 精品欧美一区免费观看α√| 亚洲乱码国产一区三区| 色www免费视频| 男人天堂1024| 老司机午夜性大片| 久久亚洲国产成人精品无码区 | 人妻精品无码一区二区三区| 欧美伦理片在线看| 97人人模人人爽人人澡| 3d动漫一区二区三区| 三级视频中文字幕| 国产中文字幕二区| 91视频 - 88av| 国产福利在线免费| 国产无套粉嫩白浆内谢的出处| 精品国产乱码久久久久久1区二区| 欧美成人黄色网址| www插插插无码免费视频网站| 伊人国产精品视频| 水蜜桃色314在线观看| 97超碰免费观看| 老司机久久精品| 日韩精品aaa| av动漫免费观看| 久久国产精品免费观看| 在线免费黄色小视频| 国产又爽又黄ai换脸| 亚洲妇熟xx妇色黄蜜桃| 高清一区二区视频| 天天色综合天天色| 免费观看中文字幕| 国产老熟妇精品观看| aa在线观看视频| 中文字幕第17页| av动漫在线播放| aa在线观看视频| 手机av在线免费| 黄色激情在线视频| 一区二区在线免费看| 一道本在线免费视频| 日本一二三区视频在线| 黄色一级一级片| 免费一区二区三区在线观看| 黄页网站在线观看视频| 欧美又黄又嫩大片a级| 日韩五码在线观看| 亚洲欧美激情网| 国产综合免费视频| 美女av免费观看| 麻豆传媒网站在线观看| 粗暴91大变态调教| 女性女同性aⅴ免费观女性恋| 九一精品久久久| 免费黄频在线观看| 亚洲色图欧美自拍| 激情图片中文字幕| 日本中文字幕在线不卡| 中文字幕第一页在线视频| 人妻内射一区二区在线视频 | 日本一本中文字幕| 欧美一级黄色录像片| 欧美 另类 交| 国产曰肥老太婆无遮挡| 路边理发店露脸熟妇泻火| 日本黄色a视频| 青青青在线观看视频| 黄色小视频大全| 成人在线免费观看av| 日韩毛片在线免费看| 亚洲精品自拍网| 欧美视频亚洲图片| 人妻久久久一区二区三区| 精品视频在线观看一区| 国产男女激情视频| 欧美少妇一区二区三区| 特色特色大片在线| 国产午夜福利在线播放| 午夜福利123| 草草视频在线免费观看| 杨幂毛片午夜性生毛片| 大桥未久一区二区三区| 中文字幕乱码人妻综合二区三区| 午夜视频你懂的| 久久国产精品网| 国产又粗又猛大又黄又爽| 国产肥臀一区二区福利视频| 亚洲激情免费视频| www.色欧美| 在线观看免费不卡av| 麻豆传传媒久久久爱| 精品少妇人妻av免费久久洗澡| 九九九久久久久久久| 91香蕉视频污版| 视色视频在线观看| 污版视频在线观看| 亚洲美女爱爱视频| 激情文学亚洲色图| www.桃色.com| 九九九九九九九九| www.亚洲一区二区| 四虎4hu永久免费入口| 男女日批视频在线观看| 国产资源在线视频| 女人喷潮完整视频| 国产精品无码av在线播放| 成人毛片一区二区| 一级片视频免费观看| 四季av一区二区| youjizz.com在线观看| 久久久久久久9| 九色porny自拍| 亚洲美女自拍偷拍| 日韩欧美视频网站| 日本黄色播放器| 午夜精品久久久内射近拍高清| 超碰超碰在线观看| 久久亚洲精品无码va白人极品| 日韩精品视频一区二区在线观看| 手机在线视频一区| 免费黄色特级片| 欧美视频在线观看视频| 欧美少妇一区二区三区| 色综合色综合色综合色综合| 人妻有码中文字幕| 日韩av黄色网址| 精品少妇人妻av免费久久洗澡| 国内精品国产三级国产aⅴ久| 中文久久久久久| 91香蕉视频污版| 久久久久久久少妇| av丝袜天堂网| 亚洲国产日韩欧美在线观看| 男人插女人视频在线观看| 日本一区午夜艳熟免费| 无码中文字幕色专区| www.亚洲成人网| 日本三级免费网站| jizz欧美激情18| 亚洲天堂av免费在线观看| 一级做a爱视频| 久久视频这里有精品| 国产欧美高清在线| 亚洲黄色片免费| 日韩av中文字幕第一页| 97av视频在线观看| 在线能看的av网站| 大荫蒂性生交片| 亚洲视频在线a| 欧美精品久久久久久久自慰| 国产精品人人爽人人爽| 黄色三级视频在线播放| 50路60路老熟妇啪啪| 亚洲自拍偷拍一区二区三区| 男人天堂网视频| 97在线国产视频| 亚洲免费av网| 亚洲综合20p| 三上悠亚av一区二区三区| 欧美视频在线观看视频| 男人天堂成人在线| 国产午夜伦鲁鲁| 亚洲中文字幕无码专区| 中文字幕综合在线观看| 天天操狠狠操夜夜操| 无码内射中文字幕岛国片| 男人天堂a在线| 成人一区二区av| 亚洲国产一二三精品无码 | 加勒比海盗1在线观看免费国语版| 北条麻妃在线视频| 免费黄色一级网站| 国产成人久久777777| 91热这里只有精品| 丰满少妇在线观看| 亚洲一区在线不卡| 国产精品999.| 日本丰满少妇黄大片在线观看| 日本xxxxx18| 任你操这里只有精品| 日韩成人av免费| 97国产在线播放| 色男人天堂av| 国产精品一区二区免费在线观看| 欧美私人情侣网站| 亚洲免费视频播放| 欧美精品无码一区二区三区| 永久av免费在线观看| 日韩欧美国产综合在线| www.xxx亚洲| 亚洲色成人一区二区三区小说| 日本成人性视频| 日本成年人网址| 日韩欧美猛交xxxxx无码| 亚洲视频在线观看一区二区三区| 黄色小视频大全|