<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 開發(fā)規(guī)范

          共 8782字,需瀏覽 18分鐘

           ·

          2022-07-31 22:28


          作者:付磊 - 起揚(yáng)
          鏈接:https://yq.aliyun.com/articles/531067

          本文主要介紹在使用阿里云 Redis 的開發(fā)規(guī)范,從下面幾個方面進(jìn)行說明。

          • 鍵值設(shè)計
          • 命令使用
          • 客戶端使用
          • 相關(guān)工具
          通過本文的介紹可以減少使用 Redis 過程帶來的問題。

          一、鍵值設(shè)計

          1、key 名設(shè)計

          可讀性和可管理性
          以業(yè)務(wù)名 (或數(shù)據(jù)庫名) 為前綴(防止 key 沖突),用冒號分隔,比如業(yè)務(wù)名: 表名: id
          ugc:video:1
          簡潔性
          保證語義的前提下,控制 key 的長度,當(dāng) key 較多時,內(nèi)存占用也不容忽視,例如:
          user:{uid}:friends:messages:{mid}簡化為u:{uid}:fr:m:{mid}。
          不要包含特殊字符
          反例:包含空格、換行、單雙引號以及其他轉(zhuǎn)義字符

          2、value 設(shè)計

          拒絕 bigkey
          防止網(wǎng)卡流量、慢查詢,string 類型控制在 10KB 以內(nèi),hash、list、set、zset 元素個數(shù)不要超過 5000。
          反例:一個包含 200 萬個元素的 list。
          非字符串的 bigkey,不要使用 del 刪除,使用 hscan、sscan、zscan 方式漸進(jìn)式刪除,同時要注意防止 bigkey 過期時間自動刪除問題 (例如一個 200 萬的 zset 設(shè)置 1 小時過期,會觸發(fā) del 操作,造成阻塞,而且該操作不會不出現(xiàn)在慢查詢中 (latency 可查)),查找方法和刪除方法
          選擇適合的數(shù)據(jù)類型
          例如:實體類型 (要合理控制和使用數(shù)據(jù)結(jié)構(gòu)內(nèi)存編碼優(yōu)化配置, 例如 ziplist,但也要注意節(jié)省內(nèi)存和性能之間的平衡)
          反例:
          set user:1:name tom
          set user:1:age 19
          set user:1:favor football
          正例:
          hmset user:1 name tom age 19 favor football
          控制 key 的生命周期
          redis 不是垃圾桶,建議使用 expire 設(shè)置過期時間 (條件允許可以打散過期時間,防止集中過期),不過期的數(shù)據(jù)重點關(guān)注 idletime。

          二、命令使用

          1、O(N) 命令關(guān)注 N 的數(shù)量
          例如 hgetall、lrange、smembers、zrange、sinter 等并非不能使用,但是需要明確 N 的值。有遍歷的需求可以使用 hscan、sscan、zscan 代替。
          2、禁用命令
          禁止線上使用 keys、flushall、flushdb 等,通過 redis 的 rename 機(jī)制禁掉命令,或者使用 scan 的方式漸進(jìn)式處理。
          3、合理使用 select
          redis 的多數(shù)據(jù)庫較弱,使用數(shù)字進(jìn)行區(qū)分,很多客戶端支持較差,同時多業(yè)務(wù)用多數(shù)據(jù)庫實際還是單線程處理,會有干擾。
          4、使用批量操作提高效率
          • 原生命令:例如 mget、mset。
          • 非原生命令:可以使用 pipeline 提高效率。
          但要注意控制一次批量操作的元素個數(shù) (例如 500 以內(nèi),實際也和元素字節(jié)數(shù)有關(guān))。
          注意兩者不同:
          • 原生是原子操作,pipeline 是非原子操作。
          • pipeline 可以打包不同的命令,原生做不到
          • pipeline 需要客戶端和服務(wù)端同時支持。
          5、不建議過多使用 Redis 事務(wù)功能
          Redis 的事務(wù)功能較弱 (不支持回滾),而且集群版本(自研和官方) 要求一次事務(wù)操作的 key 必須在一個 slot 上(可以使用 hashtag 功能解決)
          6、Redis 集群版本在使用 Lua 上有特殊要求
          1、所有 key 都應(yīng)該由 KEYS 數(shù)組來傳遞,redis.call/pcall 里面調(diào)用的 redis 命令,key 的位置,必須是 KEYS array, 否則直接返回 error,"-ERR bad lua script for redis cluster, all the keys that the script uses should be passed using the KEYS arrayrn"
          2、所有 key,必須在 1 個 slot 上,否則直接返回 error, "-ERR eval/evalsha command keys must in same slotrn"
          7、monitor 命令
          必要情況下使用 monitor 命令時,要注意不要長時間使用。

          三、客戶端使用

          1、避免多個應(yīng)用使用一個 Redis 實例
          不相干的業(yè)務(wù)拆分,公共數(shù)據(jù)做服務(wù)化。
          2、使用連接池
          可以有效控制連接,同時提高效率,標(biāo)準(zhǔn)使用方式:
          執(zhí)行命令如下:
          Jedis jedis = null;
          try {
              jedis = jedisPool.getResource();
              //具體的命令
              jedis.executeCommand()
          } catch (Exception e) {
              logger.error("op key {} error: " + e.getMessage(), key, e);
          } finally {
              //注意這里不是關(guān)閉連接,在JedisPool模式下,Jedis會被歸還給資源池。
              if (jedis != null)
                  jedis.close();
          }
          3、熔斷功能
          高并發(fā)下建議客戶端添加熔斷功能 (例如 netflix hystrix)
          4、合理的加密
          設(shè)置合理的密碼,如有必要可以使用 SSL 加密訪問(阿里云 Redis 支持)
          5、淘汰策略
          根據(jù)自身業(yè)務(wù)類型,選好 maxmemory-policy(最大內(nèi)存淘汰策略),設(shè)置好過期時間。
          默認(rèn)策略是 volatile-lru,即超過最大內(nèi)存后,在過期鍵中使用 lru 算法進(jìn)行 key 的剔除,保證不過期數(shù)據(jù)不被刪除,但是可能會出現(xiàn) OOM 問題。
          其他策略如下:
          • allkeys-lru:根據(jù) LRU 算法刪除鍵,不管數(shù)據(jù)有沒有設(shè)置超時屬性,直到騰出足夠空間為止。
          • allkeys-random:隨機(jī)刪除所有鍵,直到騰出足夠空間為止。
          • volatile-random: 隨機(jī)刪除過期鍵,直到騰出足夠空間為止。
          • volatile-ttl:根據(jù)鍵值對象的 ttl 屬性,刪除最近將要過期數(shù)據(jù)。如果沒有,回退到 noeviction 策略。
          • noeviction:不會剔除任何數(shù)據(jù),拒絕所有寫入操作并返回客戶端錯誤信息 "(error) OOM command not allowed when used memory",此時 Redis 只響應(yīng)讀操作。

          四、相關(guān)工具

          1、數(shù)據(jù)同步
          redis 間數(shù)據(jù)同步可以使用:redis-port
          2、big key 搜索
          redis 大 key 搜索工具
          3、熱點 key 尋找
          內(nèi)部實現(xiàn)使用 monitor,所以建議短時間使用 facebook 的 redis-faina 阿里云 Redis 已經(jīng)在內(nèi)核層面解決熱點 key 問題

          五、刪除 bigkey

          • 下面操作可以使用 pipeline 加速。
          • redis 4.0 已經(jīng)支持 key 的異步刪除,歡迎使用。
          1、Hash 刪除: hscan + hdel
          public void delBigHash(String host, int port, String password, String bigHashKey) {
              Jedis jedis = new Jedis(host, port);
              if (password != null && !"".equals(password)) {
                  jedis.auth(password);
              }
              ScanParams scanParams = new ScanParams().count(100);
              String cursor = "0";
              do {
                  ScanResult<Entry<String, String>> scanResult = jedis.hscan(bigHashKey, cursor, scanParams);
                  List<Entry<String, String>> entryList = scanResult.getResult();
                  if (entryList != null && !entryList.isEmpty()) {
                      for (Entry<String, String> entry : entryList) {
                          jedis.hdel(bigHashKey, entry.getKey());
                      }
                  }
                  cursor = scanResult.getStringCursor();
              } while (!"0".equals(cursor));
              //刪除bigkey
              jedis.del(bigHashKey);
          }
          2、List 刪除: ltrim


          public void delBigList(String host, int port, String password, String bigListKey) {
              Jedis jedis = new Jedis(host, port);
              if (password != null && !"".equals(password)) {
                  jedis.auth(password);
              }
              long llen = jedis.llen(bigListKey);
              int counter = 0;
              int left = 100;
              while (counter < llen) {
                  //每次從左側(cè)截掉100個
                  jedis.ltrim(bigListKey, left, llen);
                  counter += left;
              }
              //最終刪除key
              jedis.del(bigListKey);
          }
          3、Set 刪除: sscan + srem
          public void delBigSet(String host, int port, String password, String bigSetKey) {
              Jedis jedis = new Jedis(host, port);
              if (password != null && !"".equals(password)) {
                  jedis.auth(password);
              }
              ScanParams scanParams = new ScanParams().count(100);
              String cursor = "0";
              do {
                  ScanResult<String> scanResult = jedis.sscan(bigSetKey, cursor, scanParams);
                  List<String> memberList = scanResult.getResult();
                  if (memberList != null && !memberList.isEmpty()) {
                      for (String member : memberList) {
                          jedis.srem(bigSetKey, member);
                      }
                  }
                  cursor = scanResult.getStringCursor();
              } while (!"0".equals(cursor));
              //刪除bigkey
              jedis.del(bigSetKey);
          }
          4、SortedSet 刪除: zscan + zrem
          public void delBigZset(String host, int port, String password, String bigZsetKey) {
              Jedis jedis = new Jedis(host, port);
              if (password != null && !"".equals(password)) {
                  jedis.auth(password);
              }
              ScanParams scanParams = new ScanParams().count(100);
              String cursor = "0";
              do {
                  ScanResult<Tuple> scanResult = jedis.zscan(bigZsetKey, cursor, scanParams);
                  List<Tuple> tupleList = scanResult.getResult();
                  if (tupleList != null && !tupleList.isEmpty()) {
                      for (Tuple tuple : tupleList) {
                          jedis.zrem(bigZsetKey, tuple.getElement());
                      }
                  }
                  cursor = scanResult.getStringCursor();
              } while (!"0".equals(cursor));
              //刪除bigkey
              jedis.del(bigZsetKey);
          }


          胖虎聯(lián)合一線大廠朋友花費8個月的時間,錄制了一份Java入門+進(jìn)階視頻教程

          課程特色:

          1. 總共88G,時常高達(dá)365小時,覆蓋所有主流技術(shù)棧

          2. 均為同一人錄制,不是東拼西湊的

          3. 對標(biāo)線下T0級別的培訓(xùn)課,講師大廠架構(gòu)師,多年授課經(jīng)驗,通俗易懂

          4. 內(nèi)容豐富,每一個技術(shù)點除了視頻,還有課堂源碼、筆記、PPT、圖解

          5. 五大實戰(zhàn)項目(視頻+源碼+筆記+SQL+軟件)

          6. 一次付費,持續(xù)更新,永無二次費用

          點擊下方超鏈接查看詳情(或者點擊文末閱讀原文):

          (點擊查看)  88G,超全技術(shù)棧的Java入門+進(jìn)階+實戰(zhàn)!

          瀏覽 18
          點贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

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

          手機(jī)掃一掃分享

          分享
          舉報
          <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>
                  人人爱人人草 | 最新中文字幕在线 | 人人妻人人干 | 人妻在线视频播放 | 豆花天天吃最新视频 |