欧美一级特黄大片做受成人-亚洲成人一区二区电影-激情熟女一区二区三区-日韩专区欧美专区国产专区

Redis和本地緩存使用的技巧有哪些

這篇文章主要介紹“redis和本地緩存使用的技巧有哪些”,在日常操作中,相信很多人在Redis和本地緩存使用的技巧有哪些問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對(duì)大家解答”Redis和本地緩存使用的技巧有哪些”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

創(chuàng)新互聯(lián)云計(jì)算的互聯(lián)網(wǎng)服務(wù)提供商,擁有超過13年的服務(wù)器租用、達(dá)州主機(jī)托管、云服務(wù)器、網(wǎng)絡(luò)空間、網(wǎng)站系統(tǒng)開發(fā)經(jīng)驗(yàn),已先后獲得國家工業(yè)和信息化部頒發(fā)的互聯(lián)網(wǎng)數(shù)據(jù)中心業(yè)務(wù)許可證。專業(yè)提供云主機(jī)、網(wǎng)絡(luò)空間、主機(jī)域名、VPS主機(jī)、云服務(wù)器、香港云服務(wù)器、免備案服務(wù)器等。

三種緩存的使用場(chǎng)景

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

Redis的使用場(chǎng)景和局限性

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

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

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

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

單(熱)點(diǎn)問題

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

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

LoadingCache的使用場(chǎng)景和局限性

對(duì)于這上面說的熱key問題,我們最直接的做法就是使用本地緩存,比如你最熟悉的guava的LoadingCache,但是使用本地緩存要求能夠接受一定的臟數(shù)據(jù),因?yàn)槿绻愀铝耸醉?,本地緩存是不?huì)更新的,它只會(huì)根據(jù)一定的過期策略來重新加載緩存,不過在我們這個(gè)場(chǎng)景是完全沒問題的,因?yàn)橐坏┰诤笈_(tái)推送了首頁后就不會(huì)再去改變了。即使改變了也沒問題,可以設(shè)置寫過期為半小時(shí),超過半小時(shí)重新加載緩存,這種短時(shí)間內(nèi)的臟數(shù)據(jù)我們是可以接受的。

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

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

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

ReloadableCache的使用場(chǎng)景和局限性

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

小結(jié)

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

  • 對(duì)于非熱點(diǎn)的數(shù)據(jù)訪問,比如用戶維度的數(shù)據(jù),直接使用redis即可;

  • 對(duì)于熱點(diǎn)數(shù)據(jù)的訪問,如果流量不是很高,無腦使用redis即可;

  • 對(duì)于熱點(diǎn)數(shù)據(jù),如果允許一定時(shí)間內(nèi)的臟數(shù)據(jù),使用LoadingCache即可;

  • 對(duì)于熱點(diǎn)數(shù)據(jù),如果一致性要求較高,同時(shí)數(shù)據(jù)量不大的情況,使用ReloadableCache即可;

小技巧

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

緩存使用的簡單介紹

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

LoadingCache的使用

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

  • 要使用load-miss的話, 要么使用V get(K key, Callable<? extends V> loader);要么使用build的時(shí)候使用的是build(CacheLoader<? super K1, V1> loader)這個(gè)時(shí)候可以直接使用get()了。此外建議使用load-miss,而不是getIfPresent==null的時(shí)候再去查數(shù)據(jù)庫,這可能導(dǎo)致緩存擊穿;

  • 使用load-miss是因?yàn)檫@是線程安全的,如果緩存失效的話,多個(gè)線程調(diào)用get的時(shí)候只會(huì)有一個(gè)線程去db查詢,其他線程需要等待,也就是說這是線程安全的。

LoadingCache<String, String> cache = CacheBuilder.newBuilder()
                .maximumSize(1000L)
                .expireAfterAccess(Duration.ofHours(1L)) // 多久不訪問就過期
                .expireAfterWrite(Duration.ofHours(1L))  // 多久這個(gè)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

reloadableCache的使用

導(dǎo)入三方依賴

<dependency>
  <groupId>com.github.phantomthief</groupId>
  <artifactId>zknotify-cache</artifactId>
  <version>0.1.22</version>
</dependency>

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

public interface ReloadableCache<T> extends Supplier<T> {

    /**
     * 獲取緩存數(shù)據(jù)
     */
    @Override
    T get();

    /**
     * 通知全局緩存更新
     * 注意:如果本地緩存沒有初始化,本方法并不會(huì)初始化本地緩存并重新加載
     *
     * 如果需要初始化本地緩存,請(qǐng)先調(diào)用 {@link ReloadableCache#get()}
     */
    void reload();

    /**
     * 更新本地緩存的本地副本
     * 注意:如果本地緩存沒有初始化,本方法并不會(huì)初始化并刷新本地的緩存
     *
     * 如果需要初始化本地緩存,請(qǐng)先調(diào)用 {@link ReloadableCache#get()}
     */
    void reloadLocal();
}

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

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

緩存擊穿

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

  • 比如使用本地緩存

  • 本地緩存使用load-miss方法

  • 使用第三方服務(wù)來加載緩存

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

那么我們是不是可以設(shè)計(jì)一個(gè)類似分布式鎖,但是更可靠的rpc服務(wù)呢?當(dāng)調(diào)用get方法的時(shí)候這個(gè)rpc服務(wù)保證相同的key打到同一個(gè)節(jié)點(diǎn),并且使用synchronized來進(jìn)行加鎖,之后完成數(shù)據(jù)的加載。在快手提供了一個(gè)叫cacheSetter的框架。下面提供一個(gè)簡易版,自己寫也很容易實(shí)現(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ù),如果部署了多臺(tái)機(jī)器那么調(diào)用端最好使用id做一致性hash保證相同id的請(qǐng)求打到同一臺(tái)機(jī)器。
 **/
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對(duì)應(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);

    /**
     * 對(duì)需要加載緩存的id綁定CountDownLatch,后續(xù)相同的id請(qǐng)求來了從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ù),此時(shí)持有相同id的線程需要等待
     * @param ids 需要加載緩存的id集合
     * @return 正在加載的id所對(duì)應(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;
    }
}

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

import java.util.Collection;
public class BizCacheSetterRpcService extends AbstractCacheSetterService {
    @Override
    protected void loadCache(Collection<String> needLoadIds) {
        // 讀取db進(jìn)行處理
    // 設(shè)置緩存
    }
}

緩存穿透

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

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

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

緩存雪崩

就是大量緩存集中失效打到了db,當(dāng)然肯定都是一類的業(yè)務(wù)緩存,歸根到底是代碼寫的有問題。可以將緩存失效的過期時(shí)間打散,別讓其集中失效就可以了。

到此,關(guān)于“Redis和本地緩存使用的技巧有哪些”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

本文題目:Redis和本地緩存使用的技巧有哪些
URL分享:http://aaarwkj.com/article12/iipigc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站建設(shè)、搜索引擎優(yōu)化商城網(wǎng)站、軟件開發(fā)App開發(fā)、網(wǎng)站內(nèi)鏈

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站制作
亚洲欧美日韩精品av| 亚洲成色在线综合剧情网站| 国产精品99久久久久久人| 午夜免费成人在线视频| 亚洲无线码一区国产欧美国日| 日本一区二区三区免费不卡视频 | 88国产精品久久久久久| 熟年人妻一区二区三区| 亚洲精品一区二区三区香蕉| av熟妇人妻一区二区三区| 午夜精品一区二区三区在线视频| 日韩女同一区二区三区在线观看| 亚洲精品一区二区三区不卡| 亚洲免费一级黄色录像片| 欧美日韩在线高清一区二区| 久久精品国产亚洲av不丁香| 日韩不卡一区二区在线观看| 中文字幕乱码av一区二区| 日韩精品在线播放观看| 午夜最新福利在线视频| 日韩精品在线观看视频一区二区三区 | 亚洲丰满熟女乱一区二区三区| 禁区正片免费看完整国产| 亚洲青涩精品一区二区三区| 国产三级成人在线视频| 色哟国产传媒视频在线观看| 国产不卡一区不卡二区| 正在播放蜜臀av在线| 欧美午夜国产在线观看| 亚洲免费成人一区二区| 男女裸体做爰一进一出视频| 国产情侣自拍在线观看| 四季一区二区三区av| 天堂在线精品亚洲综合网| 乱色熟女一区二区三区| 欧美欧美欧美欧美在线| 老女人性生交大片免费| 免费看av网站一区二区| 日韩视频专区一区二区| 亚洲熟妇中文字幕五十中出| 亚洲av午夜福利麻豆av|