<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的本地緩存組合,性能優(yōu)化最佳方案

          共 8992字,需瀏覽 18分鐘

           ·

          2022-06-20 02:05


          目錄

          • 前言

          • 設(shè)計(jì)示例

          • 后記



          前言


          我們開發(fā)中經(jīng)常用到 Redis 作為緩存,將高頻數(shù)據(jù)放在 Redis 中能夠提高業(yè)務(wù)性能,降低 MySQL 等關(guān)系型數(shù)據(jù)庫壓力,甚至一些系統(tǒng)使用 Redis 進(jìn)行數(shù)據(jù)持久化,Redis 松散的文檔結(jié)構(gòu)非常適合業(yè)務(wù)系統(tǒng)開發(fā),在精確查詢,數(shù)據(jù)統(tǒng)計(jì)業(yè)務(wù)有著很大的優(yōu)勢(shì)。

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


          設(shè)計(jì)示例


          | Redis 懶加載緩存

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


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

              @Autowired
              private RedisTemplate<String, Xx> redisTemplate;

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

              /**
               * 查詢 通過查詢緩存是否存在驅(qū)動(dòng)緩存加載 建議在前置業(yè)務(wù)保證id對(duì)應(yīng)數(shù)據(jù)是絕對(duì)存在于數(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ù)庫中有對(duì)應(yīng)數(shù)據(jù)
                  Xx xx = xxService.getXxById(id);
                  // 3.設(shè)置緩存、這一步相當(dāng)于Redis緩存懶加載,下次再查詢此id,則會(huì)走緩存
                  setXxFromCache(xx);
                  return xx;
                  }
              }

              /**
               * 對(duì)xx數(shù)據(jù)進(jìn)行修改或者刪除操作 操作數(shù)據(jù)庫成功后 刪除緩存
               * 刪除請(qǐng)求 - 刪除數(shù)據(jù)庫數(shù)據(jù) 刪除緩存
               * 修改請(qǐng)求 - 更新數(shù)據(jù)庫數(shù)據(jù) 刪除緩存 下次在查詢時(shí)候就會(huì)從數(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{
                  // 省略實(shí)現(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());
              }
          }
          // 實(shí)體類
          @Data
          public class Xx {
              // 業(yè)務(wù)主鍵
              private Long id;
              // ...省略
          }


          優(yōu)點(diǎn)如下:

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


          缺點(diǎn)如下:

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


          總結(jié):

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

          Java項(xiàng)目分享  最新整理全集


          | Redis 結(jié)合本地緩存

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


          流程圖如下:

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


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


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


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


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

          public class DeviceIncCache {

              /**
               * 本地緩存
               */

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

              @Autowired
              private RedisTemplate<String, Integer> redisTemplate;

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

              private static final String DEVICE_INC_COUNT = "device_inc_count";

              /**
               * redis設(shè)備編碼對(duì)應(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è)備生成一個(gè)自增號(hào)
                  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)點(diǎn)如下:

          • redis 保證數(shù)據(jù)可持久,本地緩存保證超高的讀取性能,微服務(wù)共用 redis 大緩存的場(chǎng)景能有效降低 redis 壓力
          • guava 作為本地緩存,提供了豐富的 api,過期策略,最大容量,保證服務(wù)內(nèi)存可控,冷數(shù)據(jù)不會(huì)長(zhǎng)期占據(jù)內(nèi)存空間
          • 服務(wù)重啟導(dǎo)致的本地緩存清空不會(huì)影響業(yè)務(wù)進(jìn)行
          • 微服務(wù)及分布式場(chǎng)景使用,分布式情況下每個(gè)服務(wù)實(shí)例只會(huì)緩存自己接入的那一部分設(shè)備的自增號(hào),本地內(nèi)存空間最優(yōu)
          • 在示例業(yè)務(wù)中,自增數(shù)滿足了分布區(qū)發(fā)送的均勻分布需求,也可以滿足統(tǒng)計(jì)設(shè)備接入數(shù)目的業(yè)務(wù)場(chǎng)景,一舉兩得


          缺點(diǎn)如下:

          • 增加編碼復(fù)雜度,不直接
          • 只適用于緩存內(nèi)容只增不改的場(chǎng)景


          總結(jié):

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



          后記

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


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


          我們充分的學(xué)習(xí)這些特性能夠幫助我們?cè)跇I(yè)務(wù)開發(fā)中更加輕松靈活,在空間與時(shí)間上找到一個(gè)平衡點(diǎn)。


          作者:熱黃油啤酒

          來源:https://juejin.cn/post/7000263632151904293


          版權(quán)申明:內(nèi)容來源網(wǎng)絡(luò),僅供分享學(xué)習(xí),版權(quán)歸原創(chuàng)者所有。除非無法確認(rèn),我們都會(huì)標(biāo)明作者及出處,如有侵權(quán)煩請(qǐng)告知,我們會(huì)立即刪除并表示歉意。謝謝!

          程序汪資料鏈接

          程序汪接的7個(gè)私活都在這里,經(jīng)驗(yàn)整理

          Java項(xiàng)目分享  最新整理全集,找項(xiàng)目不累啦 07版

          堪稱神級(jí)的Spring Boot手冊(cè),從基礎(chǔ)入門到實(shí)戰(zhàn)進(jìn)階

          臥槽!字節(jié)跳動(dòng)《算法中文手冊(cè)》火了,完整版 PDF 開放下載!

          臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開放下載!

          字節(jié)跳動(dòng)總結(jié)的設(shè)計(jì)模式 PDF 火了,完整版開放下載!


          歡迎添加程序汪個(gè)人微信 itwang009  進(jìn)粉絲群或圍觀朋友圈

          瀏覽 66
          點(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>
                  中文字幕+乱码+中文乱码视频在线观看 | 男女暧暧操逼网站视频 | 欧美日日日日 | 日韩一级在线观看免费 | 日本不卡无码视频 |