<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 很屌,不懂使用規(guī)范就糟蹋了

          共 6114字,需瀏覽 13分鐘

           ·

          2021-10-02 11:32

          ?

          這可能是最中肯的 Redis 使用規(guī)范了(ps:文末抽獎)

          碼哥,昨天我被公司 Leader 批評了。

          我在單身紅娘婚戀類型互聯(lián)網公司工作,在雙十一推出下單就送女朋友的活動。

          誰曾想,凌晨 12 點之后,用戶量暴增,出現了一個技術故障,用戶無法下單,當時老大火冒三丈!

          經過查找發(fā)現 RedisCould not get a resource from the pool

          獲取不到連接資源,并且集群中的單臺 Redis 連接量很高。

          于是各種更改最大連接數、連接等待數,雖然報錯信息頻率有所緩解,但還是持續(xù)報錯

          后來經過線下測試,發(fā)現存放 Redis 中的字符數據很大,平均 1s 返回數據

          ?

          碼哥,可以分享下使用 Redis 的規(guī)范么?我想做一個唯快不破的真男人!

          通過 Redis 為什么這么快?這篇文章我們知道 Redis 為了高性能和節(jié)省內存費勁心思。

          所以,只有規(guī)范的使用 Redis,才能實現高性能和節(jié)省內存,否則再屌的 Redis 也禁不起我們瞎折騰。

          Redis 使用規(guī)范圍繞如下幾個緯度展開:

          • 鍵值對使用規(guī)范;
          • 命令使用規(guī)范;
          • 數據保存規(guī)范;
          • 運維規(guī)范。

          點擊下方卡片,關注「碼哥字節(jié)」

          鍵值對使用規(guī)范

          有兩點需要注意:

          1. 好的 key 命名,才能提供可讀性強、可維護性高的 key,便于定位問題和尋找數據。
          2. value要避免出現 bigkey、選擇高效的序列化和壓縮、使用對象共享池、選擇高效恰當的數據類型(可參考《Redis 實戰(zhàn)篇:巧用數據類型實現億級數據統(tǒng)計》)。

          key 命名規(guī)范

          規(guī)范的 key命名,在遇到問題的時候能夠方便定位。Redis 屬于 沒有 SchemeNoSQL數據庫。

          所以要靠規(guī)范來建立其 Scheme 語意,就好比根據不同的場景我們建立不同的數據庫。

          敲黑板

          把「業(yè)務模塊名」作為前綴(好比數據庫 Scheme),通過「冒號」分隔,再加上「具體業(yè)務名」。

          這樣我們就可以通過 key 前綴來區(qū)分不同的業(yè)務數據,清晰明了。

          總結起來就是:「業(yè)務名:表名:id」

          比如我們要統(tǒng)計公眾號屬于技術類型的博主「碼哥字節(jié)」的粉絲數。

          set 公眾號:技術類:碼哥字節(jié) 100000
          ?

          碼哥,key 太長的話有什么問題么?

          key 是字符串,底層的數據結構是 SDS,SDS 結構中會包含字符串長度、分配空間大小等元數據信息。

          字符串長度增加,SDS 的元數據也會占用更多的內存空間。

          所以當字符串太長的時候,我們可以采用適當縮寫的形式。

          不要使用 bigkey

          ?

          碼哥,我就中招了,導致報錯獲取不到連接。

          因為 Redis 是單線程執(zhí)行讀寫指令,如果出現bigkey 的讀寫操作就會阻塞線程,降低 Redis 的處理效率。

          bigkey包含兩種情況:

          • 鍵值對的 value很大,比如 value保存了 2MBString數據;
          • 鍵值對的 value是集合類型,元素很多,比如保存了 5 萬個元素的 List 集合。

          雖然 Redis 官方說明了 keystring類型 value限制均為512MB

          防止網卡流量、慢查詢,string類型控制在10KB以內,hash、list、set、zset元素個數不要超過 5000。

          ?

          碼哥,如果業(yè)務數據就是這么大咋辦?比如保存的是《金瓶梅》這個大作。

          我們還可以通過 gzip 數據壓縮來減小數據大小:

          /**
           * 使用gzip壓縮字符串
           */

          public static String compress(String str) {
              if (str == null || str.length() == 0) {
                  return str;
              }

              try (ByteArrayOutputStream out = new ByteArrayOutputStream();
              GZIPOutputStream gzip = new GZIPOutputStream(out)) {
                  gzip.write(str.getBytes());
              } catch (IOException e) {
                  e.printStackTrace();
              }
              return new sun.misc.BASE64Encoder().encode(out.toByteArray());
          }

          /**
           * 使用gzip解壓縮
           */

          public static String uncompress(String compressedStr) {
              if (compressedStr == null || compressedStr.length() == 0) {
                  return compressedStr;
              }
              byte[] compressed = new sun.misc.BASE64Decoder().decodeBuffer(compressedStr);;
              String decompressed = null;
              try (ByteArrayOutputStream out = new ByteArrayOutputStream();
              ByteArrayInputStream in = new ByteArrayInputStream(compressed);
              GZIPInputStream ginzip = new GZIPInputStream(in);) {
                  byte[] buffer = new byte[1024];
                  int offset = -1;
                  while ((offset = ginzip.read(buffer)) != -1) {
                      out.write(buffer, 0, offset);
                  }
                  decompressed = out.toString();
              } catch (IOException e) {
                  e.printStackTrace();
              }
              return decompressed;
          }

          集合類型

          如果集合類型的元素的確很多,我們可以將一個大集合拆分成多個小集合來保存。

          使用高效序列化和壓縮方法

          為了節(jié)省內存,我們可以使用高效的序列化方法和壓縮方法去減少 value的大小。

          protostuffkryo這兩種序列化方法,就要比 Java內置的序列化方法效率更高。

          上述的兩種序列化方式雖然省內存,但是序列化后都是二進制數據,可讀性太差。

          通常我們會序列化成 JSON或者 XML,為了避免數據占用空間大,我們可以使用壓縮工具(snappy、 gzip)將數據壓縮再存到 Redis 中。

          使用整數對象共享池

          Redis 內部維護了 0 到 9999 這 1 萬個整數對象,并把這些整數作為一個共享池使用。

          即使大量鍵值對保存了 0 到 9999 范圍內的整數,在 Redis 實例中,其實只保存了一份整數對象,可以節(jié)省內存空間。

          需要注意的是,有兩種情況是不生效的:

          1. Redis 中設置了 maxmemory,而且啟用了 LRU策略(allkeys-lru 或 volatile-lru 策略),那么,整數對象共享池就無法使用了。

            ?

            這是因為 LRU 需要統(tǒng)計每個鍵值對的使用時間,如果不同的鍵值對都復用一個整數對象就無法統(tǒng)計了。

          2. 如果集合類型數據采用 ziplist 編碼,而集合元素是整數,這個時候,也不能使用共享池。

            ?

            因為 ziplist 使用了緊湊型內存結構,判斷整數對象的共享情況效率低。

          命令使用規(guī)范

          有的命令的執(zhí)行會造成很大的性能問題,我們需要格外注意。

          生產禁用的指令

          Redis 是單線程處理請求操作,如果我們執(zhí)行一些涉及大量操作、耗時長的命令,就會嚴重阻塞主線程,導致其它請求無法得到正常處理。

          • KEYS:該命令需要對 Redis 的全局哈希表進行全表掃描,嚴重阻塞 Redis 主線程;

            ?

            應該使用 SCAN 來代替,分批返回符合條件的鍵值對,避免主線程阻塞。

          • FLUSHALL:刪除 Redis 實例上的所有數據,如果數據量很大,會嚴重阻塞 Redis 主線程;

          • FLUSHDB,刪除當前數據庫中的數據,如果數據量很大,同樣會阻塞 Redis 主線程。

            ?

            加上 ASYNC 選項,讓 FLUSHALL,FLUSHDB 異步執(zhí)行。

          我們也可以直接禁用,用rename-command命令在配置文件中對這些命令進行重命名,讓客戶端無法使用這些命令。

          慎用 MONITOR 命令

          MONITOR 命令會把監(jiān)控到的內容持續(xù)寫入輸出緩沖區(qū)。

          如果線上命令的操作很多,輸出緩沖區(qū)很快就會溢出了,這就會對 Redis 性能造成影響,甚至引起服務崩潰。

          所以,除非十分需要監(jiān)測某些命令的執(zhí)行(例如,Redis 性能突然變慢,我們想查看下客戶端執(zhí)行了哪些命令)我們才使用。

          慎用全量操作命令

          比如獲取集合中的所有元素(HASH 類型的 hgetall、List 類型的 lrange、Set 類型的 smembers、zrange 等命令)。

          這些操作會對整個底層數據結構進行全量掃描 ,導致阻塞 Redis 主線程。

          ?

          碼哥,如果業(yè)務場景就是需要獲取全量數據咋辦?

          有兩個方式可以解決:

          1. 使用 SSCAN、HSCAN等命令分批返回集合數據;
          2. 把大集合拆成小集合,比如按照時間、區(qū)域等劃分。

          數據保存規(guī)范

          冷熱數據分離

          雖然 Redis 支持使用 RDB 快照和 AOF 日志持久化保存數據,但是,這兩個機制都是用來提供數據可靠性保證的,并不是用來擴充數據容量的。

          不要什么數據都存在 Redis,應該作為緩存保存熱數據,這樣既可以充分利用 Redis 的高性能特性,還可以把寶貴的內存資源用在服務熱數據上。

          業(yè)務數據隔離

          不要將不相關的數據業(yè)務都放到一個 Redis 中。一方面避免業(yè)務相互影響,另一方面避免單實例膨脹,并能在故障時降低影響面,快速恢復。

          設置過期時間

          在數據保存時,我建議你根據業(yè)務使用數據的時長,設置數據的過期時間。

          寫入 Redis 的數據會一直占用內存,如果數據持續(xù)增多,就可能達到機器的內存上限,造成內存溢出,導致服務崩潰。

          控制單實例的內存容量

          建議設置在 2~6 GB 。這樣一來,無論是 RDB 快照,還是主從集群進行數據同步,都能很快完成,不會阻塞正常請求的處理。

          防止緩存雪崩

          避免集中過期 key 導致緩存雪崩。

          ?

          碼哥,什么是緩存雪崩?

          當某一個時刻出現大規(guī)模的緩存失效的情況,那么就會導致大量的請求直接打在數據庫上面,導致數據庫壓力巨大,如果在高并發(fā)的情況下,可能瞬間就會導致數據庫宕機。

          運維規(guī)范

          1. 使用 Cluster 集群或者哨兵集群,做到高可用;
          2. 實例設置最大連接數,防止過多客戶端連接導致實例負載過高,影響性能。
          3. 不開啟 AOF 或開啟 AOF 配置為每秒刷盤,避免磁盤 IO 拖慢 Redis 性能。
          4. 設置合理的 repl-backlog,降低主從全量同步的概率
          5. 設置合理的 slave client-output-buffer-limit,避免主從復制中斷情況發(fā)生。
          6. 根據實際場景設置合適的內存淘汰策略。
          7. 使用連接池操作 Redis。

          推薦閱讀:
          聊聊分布式鎖——Redis和Redisson的方式
          面霸篇:高頻 Java 基礎問題(核心卷一)
          面霸篇:Java 核心集合容器全解(核心卷二)
          這個 MySQL bug 99% 的人會踩坑!


          互聯(lián)網全棧架構

          瀏覽 31
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <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>
                  在线白浆 | 无码乱伦中文字幕 | 日韩中文字幕有码 | 日韩操逼图片 | 日韩欧美成人网站 |