1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        如何防止緩存擊穿?

        共 2911字,需瀏覽 6分鐘

         ·

        2020-09-23 18:28

        點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

        優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

        ? 作者?|??zheski

        來(lái)源 |? urlify.cn/a6zeIj

        66套java從入門(mén)到精通實(shí)戰(zhàn)課程分享

        緩存擊穿

        在使用緩存時(shí),我們往往是先根據(jù)key從緩存中取數(shù)據(jù),如果拿不到就去數(shù)據(jù)源加載數(shù)據(jù),寫(xiě)入緩存。但是在某些高并發(fā)的情況下,可能會(huì)出現(xiàn)緩存擊穿的問(wèn)題,比如一個(gè)存在的key,在緩存過(guò)期的一刻,同時(shí)有大量的請(qǐng)求,這些請(qǐng)求都會(huì)擊穿到DB,造成瞬時(shí)DB請(qǐng)求量大、壓力驟增。

        一般解決方案

        首先我們想到的解決方案就是加鎖,一種辦法是:拿到鎖的請(qǐng)求,去加載數(shù)據(jù),沒(méi)有拿到鎖的請(qǐng)求,就先等待。這種方法雖然避免了并發(fā)加載數(shù)據(jù),但實(shí)際上是將并發(fā)的操作串行化,會(huì)增加系統(tǒng)延時(shí)。

        singleflight

        singleflight是groupcache這個(gè)項(xiàng)目的一部分,groupcache是memcache作者使用golang編寫(xiě)的分布式緩存。singleflight能夠使多個(gè)并發(fā)請(qǐng)求的回源操作中,只有第一個(gè)請(qǐng)求會(huì)進(jìn)行回源操作,其他的請(qǐng)求會(huì)阻塞等待第一個(gè)請(qǐng)求完成操作,直接取其結(jié)果,這樣可以保證同一時(shí)刻只有一個(gè)請(qǐng)求在進(jìn)行回源操作,從而達(dá)到防止緩存擊穿的效果。下面是參考groupcache源碼,使用Java實(shí)現(xiàn)的singleflight代碼:

        //代表正在進(jìn)行中,或已經(jīng)結(jié)束的請(qǐng)求
        public?class?Call?{
        ????private?byte[]?val;
        ????private?CountDownLatch?cld;

        ????public?byte[]?getVal()?{
        ????????return?val;
        ????}

        ????public?void?setVal(byte[]?val)?{
        ????????this.val?=?val;
        ????}

        ????public?void?await()?{
        ????????try?{
        ????????????this.cld.await();
        ????????}?catch?(InterruptedException?e)?{
        ????????????e.printStackTrace();
        ????????}
        ????}

        ????public?void?lock()?{
        ????????this.cld?=?new?CountDownLatch(1);
        ????}

        ????public?void?done()?{
        ????????this.cld.countDown();
        ????}
        }
        //singleflight?的主類(lèi),管理不同?key?的請(qǐng)求(call)
        public?class?CallManage?{
        ????private?final?Lock?lock?=?new?ReentrantLock();
        ????private?Map?callMap;

        ????public?byte[]?run(String?key,?Supplier?func)?{
        ????????this.lock.lock();
        ????????if?(this.callMap?==?null)?{
        ????????????this.callMap?=?new?HashMap<>();
        ????????}
        ????????Call?call?=?this.callMap.get(key);
        ????????if?(call?!=?null)?{
        ????????????this.lock.unlock();
        ????????????call.await();
        ????????????return?call.getVal();
        ????????}
        ????????call?=?new?Call();
        ????????call.lock();
        ????????this.callMap.put(key,?call);
        ????????this.lock.unlock();

        ????????call.setVal(func.get());
        ????????call.done();

        ????????this.lock.lock();
        ????????this.callMap.remove(key);
        ????????this.lock.unlock();

        ????????return?call.getVal();
        ????}
        }

        我們使用CountDownLatch來(lái)實(shí)現(xiàn)多個(gè)線(xiàn)程等待一個(gè)線(xiàn)程完成操作,CountDownLatch包含一個(gè)計(jì)數(shù)器,初始化時(shí)賦值,countDown()可使計(jì)數(shù)器減一,當(dāng)count為0時(shí)喚醒所有等待的線(xiàn)程,await()可使線(xiàn)程阻塞。我們同樣用CountDownLatch來(lái)模擬一個(gè)10次并發(fā),測(cè)試代碼如下:

        public?static?void?main(String[]?args)?{
        ????CallManage?callManage?=?new?CallManage();
        ????int?count?=?10;
        ????CountDownLatch?cld?=?new?CountDownLatch(count);
        ????for?(int?i?=?0;?i?????????new?Thread(()?->?{
        ????????????try?{
        ????????????????cld.await();
        ????????????}?catch?(InterruptedException?e)?{
        ????????????????e.printStackTrace();
        ????????????}
        ????????????byte[]?value?=?callManage.run("key",?()?->?{
        ????????????????System.out.println("func");
        ????????????????return?ByteArrayUtil.oToB("bar");
        ????????????});
        ????????????System.out.println(ByteArrayUtil.bToO(value).toString());
        ????????}).start();
        ????????cld.countDown();
        ????}
        }

        測(cè)試結(jié)果如下:

        func
        bar
        bar
        bar
        bar
        bar
        bar
        bar
        bar
        bar
        bar

        可以看到回源操作只被執(zhí)行了一次,其他9次直接取到了第一次操作的結(jié)果。

        總結(jié)

        可以看到singleflight可以有效解決高并發(fā)情況下的緩存擊穿問(wèn)題,singleflight這種控制機(jī)制不僅可以用在緩存擊穿的問(wèn)題上,理論上可以解決各種分層結(jié)構(gòu)的高并發(fā)性能問(wèn)題。




        粉絲福利:108本java從入門(mén)到大神精選電子書(shū)領(lǐng)取

        ???

        ?長(zhǎng)按上方鋒哥微信二維碼?2 秒
        備注「1234」即可獲取資料以及
        可以進(jìn)入java1234官方微信群



        感謝點(diǎn)贊支持下哈?


        瀏覽 43
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            大香蕉伊人在线观看视频 | 91成人无码人妻一区二区 | sese在线| 骚逼自拍| 美女日日夜夜综合 | 中国xxxxxxxxx老师 | 男女洗澡一边做一边摸 | 精品一区二区三区四区五区六区 | 性猛交一级A片少妇视频无码 | 狠狠色噜噜狠狠狠狠色综合久97 |