<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何防止緩存擊穿?

          共 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從入門到精通實(shí)戰(zhàn)課程分享

          緩存擊穿

          在使用緩存時(shí),我們往往是先根據(jù)key從緩存中取數(shù)據(jù),如果拿不到就去數(shù)據(jù)源加載數(shù)據(jù),寫入緩存。但是在某些高并發(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編寫的分布式緩存。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?的主類,管理不同?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è)線程等待一個(gè)線程完成操作,CountDownLatch包含一個(gè)計(jì)數(shù)器,初始化時(shí)賦值,countDown()可使計(jì)數(shù)器減一,當(dāng)count為0時(shí)喚醒所有等待的線程,await()可使線程阻塞。我們同樣用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從入門到大神精選電子書領(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)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天视频有没有黄色。 | 99精品一级毛片 | 亚洲天天干 | 日本一级黃色大片看免费 | 亚洲成人精品 |