<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>

          Redis+Guava,性能炸裂!

          共 8956字,需瀏覽 18分鐘

           ·

          2022-06-26 19:57

          點擊關(guān)注公眾號:互聯(lián)網(wǎng)架構(gòu)師,后臺回復(fù) 2T獲取2TB學(xué)習(xí)資源!
          上一篇:Alibaba開源內(nèi)網(wǎng)高并發(fā)編程手冊.pdf
          前言
          我們開發(fā)中經(jīng)常用到 Redis 作為緩存,將高頻數(shù)據(jù)放在 Redis 中能夠提高業(yè)務(wù)性能,降低 MySQL 等關(guān)系型數(shù)據(jù)庫壓力,甚至一些系統(tǒng)使用 Redis 進行數(shù)據(jù)持久化,Redis 松散的文檔結(jié)構(gòu)非常適合業(yè)務(wù)系統(tǒng)開發(fā),在精確查詢,數(shù)據(jù)統(tǒng)計業(yè)務(wù)有著很大的優(yōu)勢。

          但是高頻數(shù)據(jù)流處理系統(tǒng)中,Redis 的壓力也會很大,同時 I/O 開銷才是耗時的主要原因,這時候為了降低 Redis 讀寫壓力我們可以用到本地緩存,Guava 為我們提供了優(yōu)秀的本地緩存 API,包含了過期策略等等,編碼難度低,個人非常推薦。


          設(shè)計示例

          Redis 懶加載緩存

          數(shù)據(jù)在新增到 MySQL 不進行緩存,在精確查找進行緩存,做到查詢即緩存,不查詢不緩存。


          流程圖如下:
          代碼示例:
          // 偽代碼示例 Xx代表你的的業(yè)務(wù)對象 如User Goods等等
          public class XxLazyCache {

              @Autowired
              private RedisTemplate<String, Xx> redisTemplate;

              @Autowired
              private XxService xxService;// 你的業(yè)務(wù)service

              /**
               * 查詢 通過查詢緩存是否存在驅(qū)動緩存加載 建議在前置業(yè)務(wù)保證id對應(yīng)數(shù)據(jù)是絕對存在于數(shù)據(jù)庫中的
               */

              public Xx getXx(int id{
                  // 1.查詢緩存里面有沒有數(shù)據(jù)
                  Xx xxCache = getXxFromCache(id);
                  if(xxCache != null) {
                      return xxCache;// 衛(wèi)語句使代碼更有利于閱讀
                  }
                  // 2.查詢數(shù)據(jù)庫獲取數(shù)據(jù) 我們假定到業(yè)務(wù)這一步,傳過來的id都在數(shù)據(jù)庫中有對應(yīng)數(shù)據(jù)
                  Xx xx = xxService.getXxById(id);
                  // 3.設(shè)置緩存、這一步相當(dāng)于Redis緩存懶加載,下次再查詢此id,則會走緩存
                  setXxFromCache(xx);
                  return xx;
                  }
              }

              /**
               * 對xx數(shù)據(jù)進行修改或者刪除操作 操作數(shù)據(jù)庫成功后 刪除緩存
               * 刪除請求 - 刪除數(shù)據(jù)庫數(shù)據(jù) 刪除緩存
               * 修改請求 - 更新數(shù)據(jù)庫數(shù)據(jù) 刪除緩存 下次在查詢時候就會從數(shù)據(jù)庫拉取新的數(shù)據(jù)到緩存中
               */

              public void deleteXxFromCache(long id{
                  String key = "Xx:" + xx.getId();
                  redisTemplate.delete(key);
              }

              private void setXxFromCache(Xx xx{
                  String key = "Xx:" + xx.getId();
                  redisTemplate.opsForValue().set(key, xx);
              }

              private Xx getXxFromCache(int id{
                  // 通過緩存前綴拼裝唯一主鍵作為緩存Key 如Xxx信息 就是Xxx:id
                  String key = "Xx:" + id;
                  return redisTemplate.opsForValue().get(key);
              }

          }
          // 業(yè)務(wù)類
          public class XxServie {
              @Autowired
              private XxLazyCache xxLazyCache;
              // 查詢數(shù)據(jù)庫
              public Xx getXxById(long id{
                  // 省略實現(xiàn)
                  return xx;
              }

              public void updateXx(Xx xx{
                  // 更新MySQL數(shù)據(jù) 省略
                  // 刪除緩存
                  xxLazyCache.deleteXxFromCache(xx.getId());
              }

              public void deleteXx(long id{
                  // 刪除MySQL數(shù)據(jù) 省略
                  // 刪除緩存
                  xxLazyCache.deleteXxFromCache(xx.getId());
              }
          }
          // 實體類
          @Data
          public class Xx {
              // 業(yè)務(wù)主鍵
              private Long id;
              // ...省略
          }


          優(yōu)點如下:
          • 保證最小的緩存量滿足精確查詢業(yè)務(wù),避免冷數(shù)據(jù)占用寶貴的內(nèi)存空間
          • 對增刪改查業(yè)務(wù)入侵小、刪除即同步
          • 可插拔,對于老系統(tǒng)升級,歷史數(shù)據(jù)無需在啟動時初始化緩存


          缺點如下:
          • 數(shù)據(jù)量需可控,在無限增長業(yè)務(wù)場景不適用
          • 在微服務(wù)場景不利于全局緩存應(yīng)用


          總結(jié):
          • 空間最小化
          • 滿足精確查詢場景
          • 總數(shù)據(jù)量可控推薦使用
          • 微服務(wù)場景不適用


          Redis 結(jié)合本地緩存

          微服務(wù)場景下,多個微服務(wù)使用一個大緩存,流數(shù)據(jù)業(yè)務(wù)下,高頻讀取緩存對 Redis 壓力很大,我們使用本地緩存結(jié)合 Redis 緩存使用,降低 Redis 壓力,同時本地緩存沒有連接開銷,性能更優(yōu)。


          流程圖如下:
          業(yè)務(wù)場景:在流處數(shù)處理過程中,微服務(wù)對多個設(shè)備上傳的數(shù)據(jù)進行處理,每個設(shè)備有一個 code,流數(shù)據(jù)的頻率高,在消息隊列發(fā)送過程中使用分區(qū)發(fā)送,我們需要為設(shè)備 code 生成對應(yīng)的自增號,用自增號對 kafka 中 topic 分區(qū)數(shù)進行取模。


          這樣如果有 10000 臺設(shè)備,自增號就是 0~9999,在取模后就進行分區(qū)發(fā)送就可以做到每個分區(qū)均勻分布。


          這個自增號我們使用 redis 的自增數(shù)生成,生成后放到 redis 的 hash 結(jié)構(gòu)進行緩存,每次來一個設(shè)備,我們就去這個 hash 緩存中取,沒有取到就使用自增數(shù)生成一個,然后放到 redis 的 hash 緩存中。


          這時候每個設(shè)備的自增數(shù)一經(jīng)生成是不會再發(fā)生改變的,我們就想到使用本地緩存進行優(yōu)化,避免高頻的調(diào)用 redis 去獲取,降低 redis 壓力。


          代碼示例:

          /**
           * 此緩存演示如何結(jié)合redis自增數(shù) hash 本地緩存使用進行設(shè)備自增數(shù)的生成、緩存、本地緩存
           * 本地緩存使用Guava Cache
           */

          public class DeviceIncCache {

              /**
               * 本地緩存
               */

              private Cache<String, Integer> localCache = CacheBuilder.newBuilder()
                  .concurrencyLevel(16// 并發(fā)級別
                  .initialCapacity(1000// 初始容量
                  .maximumSize(10000// 緩存最大長度
                  .expireAfterAccess(1, TimeUnit.HOURS) // 緩存1小時沒被使用就過期
                  .build();

              @Autowired
              private RedisTemplate<String, Integer> redisTemplate;

              /**
               * redis自增數(shù)緩存的key
               */

              private static final String DEVICE_INC_COUNT = "device_inc_count";

              /**
               * redis設(shè)備編碼對應(yīng)自增數(shù)的hash緩存key
               */

              private static final String DEVICE_INC_VALUE = "device_inc_value";

              /**
               * 獲取設(shè)備自增數(shù)
               */

              public int getInc(String deviceCode){
                  // 1.從本地緩存獲取
                  Integer inc = localCache.get(deviceCode);
                  if(inc != null) {
                      return inc;
                  }
                  // 2.本地緩存未命中,從redis的hash緩存獲取
                  inc = (Integer)redisTemplate.opsForHash().get(DEVICE_INC_VALUE, deviceCode);
                  // 3. redis的hash緩存中沒有,說明是新設(shè)備,先為設(shè)備生成一個自增號
                  if(inc == null) {
                      inc = redisTemplate.opsForValue().increment(DEVICE_INC_COUNT).intValue;
                      // 添加到redis hash緩存
                      redisTemplate.opsForHash().put(DEVICE_INC_VALUE, deviceCode, inc);
                  }
                  // 4.添加到本地緩存
                  localCache.put(deviceCode, inc);
                  // 4.返回自增數(shù)
                  return inc;
              }

          }


          優(yōu)點如下:
          • redis 保證數(shù)據(jù)可持久,本地緩存保證超高的讀取性能,微服務(wù)共用 redis 大緩存的場景能有效降低 redis 壓力

          • guava 作為本地緩存,提供了豐富的 api,過期策略,最大容量,保證服務(wù)內(nèi)存可控,冷數(shù)據(jù)不會長期占據(jù)內(nèi)存空間

          • 服務(wù)重啟導(dǎo)致的本地緩存清空不會影響業(yè)務(wù)進行

          • 微服務(wù)及分布式場景使用,分布式情況下每個服務(wù)實例只會緩存自己接入的那一部分設(shè)備的自增號,本地內(nèi)存空間最優(yōu)

          • 在示例業(yè)務(wù)中,自增數(shù)滿足了分布區(qū)發(fā)送的均勻分布需求,也可以滿足統(tǒng)計設(shè)備接入數(shù)目的業(yè)務(wù)場景,一舉兩得


          缺點如下:
          • 增加編碼復(fù)雜度,不直接
          • 只適用于緩存內(nèi)容只增不改的場景


          總結(jié):
          • 本地緩存空間可控,過期策略優(yōu)
          • 適用于微服務(wù)及分布式場景
          • 緩存內(nèi)容不能發(fā)生改變
          • 性能優(yōu)


          后記
          redis 提供了豐富的數(shù)據(jù)類型及 api,非常適合業(yè)務(wù)系統(tǒng)開發(fā),統(tǒng)計計數(shù)(increment,decrement),標(biāo)記位(bitmap),松散數(shù)據(jù)(hash),先進先出、隊列式讀取(list)。

          guava 緩存作為本地緩存,能夠高效的讀取的同時,提供了大量 api 方便我們控制本地緩存的數(shù)據(jù)量及冷數(shù)據(jù)淘汰。

          作者:熱黃油啤酒 

          鏈接:https://juejin.cn/post/7000263632151904293


          -End-
          最后,關(guān)注公眾號互聯(lián)網(wǎng)架構(gòu)師,在后臺回復(fù):2T,可以獲取我整理的 Java 系列面試題和答案,非常齊全


          正文結(jié)束


          推薦閱讀 ↓↓↓

          1.心態(tài)崩了!稅前2萬4,到手1萬4,年終獎扣稅方式1月1日起施行~

          2.深圳一普通中學(xué)老師工資單曝光,秒殺程序員,網(wǎng)友:敢問是哪個學(xué)校畢業(yè)的?

          3.從零開始搭建創(chuàng)業(yè)公司后臺技術(shù)棧

          4.程序員一般可以從什么平臺接私活?

          5.清華大學(xué):2021 元宇宙研究報告!

          6.為什么國內(nèi) 996 干不過國外的 955呢?

          7.這封“領(lǐng)導(dǎo)痛批95后下屬”的郵件,句句扎心!

          8.15張圖看懂瞎忙和高效的區(qū)別!

          瀏覽 30
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  一级黄色免费观看 | 国产精品综合小视频 | 久久综合五月天 | 熟女av在线 | 一呦二呦三呦精品网站 |