• <acronym id="qmqcg"><cite id="qmqcg"></cite></acronym>
    <td id="qmqcg"><em id="qmqcg"></em></td>
    • 首頁 > 生活 >

      Redis緩存空間優化實踐詳解

      目錄
      導讀場景設定常規做法改進1-去掉屬性名改進2-使用更好的序列化工具改進3-優化數據類型改進4-考慮ZIP壓縮最終落地場景延伸

      導讀

      緩存Redis,是我們最常用的服務,其適用場景廣泛,被大量應用到各業務場景中。也正因如此,緩存成為了重要的硬件成本來源,我們有必要從空間上做一些優化,降低成本的同時也會提高性能。

      下面以我們的案例說明,將緩存空間減少70%的做法。


      (資料圖片僅供參考)

      場景設定

      1、我們需要將POJO存儲到緩存中,該類定義如下

      public class TestPOJO implements Serializable {
          private String testStatus;
          private String userPin;
          private String investor;
          private Date testQueryTime;
          private Date createTime;
          private String bizInfo;
          private Date otherTime;
          private BigDecimal userAmount;
          private BigDecimal userRate;
          private BigDecimal applyAmount;
          private String type;
          private String checkTime;
          private String preTestStatus;
          
          public Object[] toValueArray(){
              Object[] array = {testStatus, userPin, investor, testQueryTime,
                      createTime, bizInfo, otherTime, userAmount,
                      userRate, applyAmount, type, checkTime, preTestStatus};
              return array;
          }
          
          public CreditRecord fromValueArray(Object[] valueArray){         
              //具體的數據類型會丟失,需要做處理
          }
      }

      2、用下面的實例作為測試數據

      TestPOJO pojo = new TestPOJO();
      pojo.setApplyAmount(new BigDecimal("200.11"));
      pojo.setBizInfo("XX");
      pojo.setUserAmount(new BigDecimal("1000.00"));
      pojo.setTestStatus("SUCCESS");
      pojo.setCheckTime("2023-02-02");
      pojo.setInvestor("ABCD");
      pojo.setUserRate(new BigDecimal("0.002"));
      pojo.setTestQueryTime(new Date());
      pojo.setOtherTime(new Date());
      pojo.setPreTestStatus("PROCESSING");
      pojo.setUserPin("ABCDEFGHIJ");
      pojo.setType("Y");

      常規做法

      System.out.println(JSON.toJSONString(pojo).length());

      使用JSON直接序列化、打印 length=284**,**這種方式是最簡單的方式,也是最常用的方式,具體數據如下:

      {"applyAmount":200.11,"bizInfo":"XX","checkTime":"2023-02-02","investor":"ABCD","otherTime":"2023-04-10 17:45:17.717","preCheckStatus":"PROCESSING","testQueryTime":"2023-04-10 17:45:17.717","testStatus":"SUCCESS","type":"Y","userAmount":1000.00,"userPin":"ABCDEFGHIJ","userRate":0.002}

      我們發現,以上包含了大量無用的數據,其中屬性名是沒有必要存儲的。

      改進1-去掉屬性名

      System.out.println(JSON.toJSONString(pojo.toValueArray()).length());

      通過選擇數組結構代替對象結構,去掉了屬性名,打印 length=144,將數據大小降低了50%,具體數據如下:

      ["SUCCESS","ABCDEFGHIJ","ABCD","2023-04-10 17:45:17.717",null,"XX","2023-04-10 17:45:17.717",1000.00,0.002,200.11,"Y","2023-02-02","PROCESSING"]

      我們發現,null是沒有必要存儲的,時間的格式被序列化為字符串,不合理的序列化結果,導致了數據的膨脹,所以我們應該選用更好的序列化工具。

      改進2-使用更好的序列化工具

      //我們仍然選取JSON格式,但使用了第三方序列化工具
      System.out.println(new ObjectMapper(new MessagePackFactory()).writeValueAsBytes(pojo.toValueArray()).length);

      選取更好的序列化工具,實現字段的壓縮和合理的數據格式,打印 **length=92,**空間比上一步又降低了40%。

      這是一份二進制數據,需要以二進制操作Redis,將二進制轉為字符串后,打印如下:

      ??SUCCESS?ABCDEFGHIJ?ABCD??j?6???XX??j?6?????`bM????@i??Q?Y?2023-02-02?PROCESSING

      順著這個思路再深挖,我們發現,可以通過手動選擇數據類型,實現更極致的優化效果,選擇使用更小的數據類型,會獲得進一步的提升。

      改進3-優化數據類型

      在以上用例中,testStatus、preCheckStatus、investor這3個字段,實際上是枚舉字符串類型,如果能夠使用更簡單數據類型(比如byte或者int等)替代string,還可以進一步節省空間。其中checkTime可以用Long類型替代字符串,會被序列化工具輸出更少的字節。

      public Object[] toValueArray(){
          Object[] array = {toInt(testStatus), userPin, toInt(investor), testQueryTime,
          createTime, bizInfo, otherTime, userAmount,
          userRate, applyAmount, type, toLong(checkTime), toInt(preTestStatus)};
          return array;
      }

      在手動調整后,使用了更小的數據類型替代了String類型,打印 length=69

      改進4-考慮ZIP壓縮

      除了以上的幾點之外,還可以考慮使用ZIP壓縮方式獲取更小的體積,在內容較大或重復性較多的情況下,ZIP壓縮的效果明顯,如果存儲的內容是TestPOJO的數組,可能適合使用ZIP壓縮。

      但ZIP壓縮并不一定會減少體積,在小于30個字節的情況下,也許還會增加體積。在重復性內容較少的情況下,無法獲得明顯提升。并且存在CPU開銷。

      在經過以上優化之后,ZIP壓縮不再是必選項,需要根據實際數據做測試才能分辨到ZIP的壓縮效果。

      最終落地

      上面的幾個改進步驟體現了優化的思路,但是反序列化的過程會導致類型的丟失,處理起來比較繁瑣,所以我們還需要考慮反序列化的問題。

      在緩存對象被預定義的情況下,我們完全可以手動處理每個字段,所以在實戰中,推薦使用手動序列化達到上述目的,實現精細化的控制,達到最好的壓縮效果和最小的性能開銷。

      可以參考以下msgpack的實現代碼,以下為測試代碼,請自行封裝更好的Packer和UnPacker等工具:

          
          org.msgpack    
          msgpack-core    
          0.9.3
      
      public byte[] toByteArray() throws Exception {
              MessageBufferPacker packer = MessagePack.newDefaultBufferPacker();
              toByteArray(packer);
              packer.close();
              return packer.toByteArray();
          }
      
          public void toByteArray(MessageBufferPacker packer) throws Exception {
              if (testStatus == null) {
                  packer.packNil();
              }else{
                  packer.packString(testStatus);
              }
      
              if (userPin == null) {
                  packer.packNil();
              }else{
                  packer.packString(userPin);
              }
      
              if (investor == null) {
                  packer.packNil();
              }else{
                  packer.packString(investor);
              }
      
              if (testQueryTime == null) {
                  packer.packNil();
              }else{
                  packer.packLong(testQueryTime.getTime());
              }
      
              if (createTime == null) {
                  packer.packNil();
              }else{
                  packer.packLong(createTime.getTime());
              }
      
              if (bizInfo == null) {
                  packer.packNil();
              }else{
                  packer.packString(bizInfo);
              }
      
              if (otherTime == null) {
                  packer.packNil();
              }else{
                  packer.packLong(otherTime.getTime());
              }
      
              if (userAmount == null) {
                  packer.packNil();
              }else{
                  packer.packString(userAmount.toString());
              }
      
              if (userRate == null) {
                  packer.packNil();
              }else{
                  packer.packString(userRate.toString());
              }
      
              if (applyAmount == null) {
                  packer.packNil();
              }else{
                  packer.packString(applyAmount.toString());
              }
      
              if (type == null) {
                  packer.packNil();
              }else{
                  packer.packString(type);
              }
      
              if (checkTime == null) {
                  packer.packNil();
              }else{
                  packer.packString(checkTime);
              }
      
              if (preTestStatus == null) {
                  packer.packNil();
              }else{
                  packer.packString(preTestStatus);
              }
          }
      
      
          public void fromByteArray(byte[] byteArray) throws Exception {
              MessageUnpacker unpacker = MessagePack.newDefaultUnpacker(byteArray);
              fromByteArray(unpacker);
              unpacker.close();
          }
      
          public void fromByteArray(MessageUnpacker unpacker) throws Exception {
              if (!unpacker.tryUnpackNil()){
                  this.setTestStatus(unpacker.unpackString());
              }
              if (!unpacker.tryUnpackNil()){
                  this.setUserPin(unpacker.unpackString());
              }
              if (!unpacker.tryUnpackNil()){
                  this.setInvestor(unpacker.unpackString());
              }
              if (!unpacker.tryUnpackNil()){
                  this.setTestQueryTime(new Date(unpacker.unpackLong()));
              }
              if (!unpacker.tryUnpackNil()){
                  this.setCreateTime(new Date(unpacker.unpackLong()));
              }
              if (!unpacker.tryUnpackNil()){
                  this.setBizInfo(unpacker.unpackString());
              }
              if (!unpacker.tryUnpackNil()){
                  this.setOtherTime(new Date(unpacker.unpackLong()));
              }
              if (!unpacker.tryUnpackNil()){
                  this.setUserAmount(new BigDecimal(unpacker.unpackString()));
              }
              if (!unpacker.tryUnpackNil()){
                  this.setUserRate(new BigDecimal(unpacker.unpackString()));
              }
              if (!unpacker.tryUnpackNil()){
                  this.setApplyAmount(new BigDecimal(unpacker.unpackString()));
              }
              if (!unpacker.tryUnpackNil()){
                  this.setType(unpacker.unpackString());
              }
              if (!unpacker.tryUnpackNil()){
                  this.setCheckTime(unpacker.unpackString());
              }
              if (!unpacker.tryUnpackNil()){
                  this.setPreTestStatus(unpacker.unpackString());
              }
          }

      場景延伸

      假設,我們為2億用戶存儲數據,每個用戶包含40個字段,字段key的長度是6個字節,字段是分別管理的。

      正常情況下,我們會想到hash結構,而hash結構存儲了key的信息,會占用額外資源,字段key屬于不必要數據,按照上述思路,可以使用list替代hash結構。

      通過Redis官方工具測試,使用list結構需要144G的空間,而使用hash結構需要245G的空間**(當50%以上的屬性為空時,需要進行測試,是否仍然適用)**

      在以上案例中,我們采取了幾個非常簡單的措施,僅僅有幾行簡單的代碼,可降低空間70%以上,在數據量較大以及性能要求較高的場景中,是非常值得推薦的。:

      ? 使用數組替代對象(如果大量字段為空,需配合序列化工具對null進行壓縮)

      ? 使用更好的序列化工具

      ? 使用更小的數據類型

      ? 考慮使用ZIP壓縮

      ? 使用list替代hash結構(如果大量字段為空,需要進行測試對比)

      以上就是Redis緩存空間優化實踐的詳細內容,更多關于Redis緩存空間優化的資料請關注腳本之家其它相關文章!

      關鍵詞:

      責任編輯:Rex_22

      推薦閱讀

      Redis緩存空間優化實踐詳解

      · 2023-04-22 06:23:42

      漫畫|憂心

      · 2023-04-22 04:44:36

      101披薩簡介_101披薩

      · 2023-04-22 02:38:39

      關于我們 聯系我們 商務合作 誠聘英才 網站地圖

      Copyright @ 2008-2020 www.wnchengjie.com Corporation,All Rights Reserved

      熱訊新聞網 版權所有 備案號:豫ICP備20005723號-6
      文章投訴郵箱:2 9 5 9 1 1 5 7 8@qq.com 違法信息舉報郵箱:jubao@123777.net.cn

      營業執照公示信息

      亚洲线精品久久一区二区三区,成人看片在线观看,草草视频手机在线观看视频,亚洲六月丁香色婷婷综合久久
    • <acronym id="qmqcg"><cite id="qmqcg"></cite></acronym>
      <td id="qmqcg"><em id="qmqcg"></em></td>
      • 主站蜘蛛池模板: 免费人成网站在线观看不卡 | 国产女人的高潮国语对白| 全免费a级毛片免费**视频| 久久狠狠高潮亚洲精品| 久久永久免费人妻精品| 色婷五月综激情亚洲综合| 男女免费观看在线爽爽爽视频| 第九色区AV天堂| 日出水了特别黄的视频| 国产成人精品久久综合| 亚洲国产精品尤物yw在线观看| av色综合网站| 精品一区二区三区在线观看视频 | 夜色资源站www国产在线观看| 啊灬啊灬别停啊灬用力啊免费看| 久久精品99香蕉国产| 颤声娇是什么意思| 日韩在线视频免费看| 国产日韩欧美高清| 亚洲国产欧美国产综合久久| 99久久免费精品国产72精品九九| 精品伊人久久大线蕉色首页| 打开腿给医生检查黄文| 国产亚洲av片在线观看播放| 久久精品无码午夜福利理论片| poren黑人| 欧美一区二区三区视频在线观看 | 亚洲伊人久久大香线蕉| 91av在线免费视频| 欧美老妇与ZOZOZ0交| 在线不卡免费视频| 亚洲精品自产拍在线观看| av无码免费看| 欧美性大战久久久久久| 国产精品亚洲综合一区在线观看 | 假山后面的呻吟喘息h| 一本一本久久aa综合精品| 精品少妇无码AV无码专区| 尹人香蕉久久99天天| 免费视频www| 5╳社区视频在线5sq|