国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Redis Cluster基于客戶端對mget的性能優化

數據庫
我們對客戶端JedisCluster的Multi-Key命令進行改造,通過分別對Redis節點執行pipeline操作,提升了mget命令的性能。

  • 1 背景
  • 2 分析原因
  • 2.1 現象
  • 2.2 定位問題
  • 3 解決問題
  • 3.1使用hashtag
  • 3.2 客戶端改造
  • 4 效果展示
  • 4.1 性能測試
  • 4.2 結論
  • 5 總結

一、 背景

Redis是知名的、應用廣泛的NoSQL數據庫,在轉轉也是作為主要的非關系型數據庫使用。我們主要使用Codis來管理Redis分布式集群,但隨著Codis官方停止更新和Redis Cluster的日益完善,轉轉也開始嘗試使用Redis Cluster,并選擇Lettuce作為客戶端使用。但是在業務接入過程中發現,使用Lettuce訪問Redis Cluster的mget、mset等Multi-Key命令時,性能表現不佳。

二、 分析原因

2.1 現象

業務在從Codis遷移到Redis Cluster的過程中,在Redis Cluster和Codis雙寫了相同的數據。結果Codis在比Redis Cluster多一次連接proxy節點的耗時下,同樣是mget獲取相同的數據,使用Lettuce訪問Redis Cluster還是比使用Jeds訪問Codis耗時要高,于是我們開始定位性能差異的原因。

2.2 定位問題

2.2.1 Redis Cluster的架構設計

導致Redis Cluster的mget性能不佳的根本原因,是Redis Cluster在架構上的設計導致的。Redis Cluster基于smart client和無中心的設計,按照槽位將數據存儲在不同的節點上

圖片圖片

如上圖所示,每個主節點管理不同部分的槽位,并且下面掛了多個從節點。槽位是Redis Cluster管理數據的基本單位,集群的伸縮就是槽和數據在節點之間的移動。

通過CRC16(key) % 16384計算key屬于哪個槽位和哪個Redis節點。而且Redis Cluster的Multi-Key操作受槽位限制,例如我們執行mget,獲取不同槽位的數據,是限制執行的:

圖片圖片

2.2.2 Lettuce的mget實現方式

lettuce對Multi-Key進行了支持,當我們調用mget方法,涉及跨槽位時,Lettuce對mget進行了拆分執行和結果合并,代碼如下:

public RedisFuture<List<KeyValue<K, V>>> mget(Iterable<K> keys) {
    //將key按照槽位拆分
    Map<Integer, List<K>> partitioned = SlotHash.partition(codec, keys);

    if (partitioned.size() < 2) {
        return super.mget(keys);
    }
    Map<K, Integer> slots = SlotHash.getSlots(partitioned);
    Map<Integer, RedisFuture<List<KeyValue<K, V>>>> executions = new HashMap<>();
    //對不同槽位的keys分別執行mget
    for (Map.Entry<Integer, List<K>> entry : partitioned.entrySet()) {
        RedisFuture<List<KeyValue<K, V>>> mget = super.mget(entry.getValue());
        executions.put(entry.getKey(), mget);
    }
    // 獲取、合并、排序結果
    return new PipelinedRedisFuture<>(executions, objectPipelinedRedisFuture -> {
        List<KeyValue<K, V>> result = new ArrayList<>();
        for (K opKey : keys) {
            int slot = slots.get(opKey);

            int position = partitioned.get(slot).indexOf(opKey);
            RedisFuture<List<KeyValue<K, V>>> listRedisFuture = executions.get(slot);
            result.add(MultiNodeExecution.execute(() -> listRedisFuture.get().get(position)));
        }

        return result;
    });
}

mget涉及多個key的時候,主要有三個步驟:

1、按照槽位 將key進行拆分;

2、分別對相同槽位的key去對應的槽位mget獲取數據;

3、將所有執行的結果按照傳參的key順序排序返回。

所以Lettuce客戶端,執行mget獲取跨槽位的數據,是通過槽位分發執行mget,并合并結果實現的。而Lettuce基于Netty的NIO框架實現,發送命令不會阻塞IO,但是處理請求是單連接串行發送命令:

圖片圖片

所以Lettuce的mget的key數量越多,涉及的槽位數量越多,性能就會越差。Codis也是拆分執行mget,不過是并發發送命令,并使用pipeline提高性能,進而減少了網絡的開銷。

三、 解決問題

3.1使用hashtag

我們首先想到的是 客戶端分別執行分到不同槽位的請求,導致耗時增加。我們可以將我們需要同時操作到的key,放到同一個槽位里去。我們是可以通過hashtag來實現

hashtag用于Redis Cluster中。hashtag 規定以key里{}里的內容來做hash,比如 user:{a}:zhangsan和user:{a}:lisi就會用a去hash,保證帶{a}的key都落到同一個slot里

利用hashtag對key進行規劃,使得我們mget的值都在同一個槽位里。

圖片圖片

但是這種方式需要業務方感知到Redis Cluster的分片的存在,需要對Redis Cluster的各節點存儲做規劃,保證數據平均的分布在不同的Redis節點上,對業務方使用上太不友好,所以舍棄了這種方案。

3.2 客戶端改造

另一種方案是在客戶端做改造,這樣做成本較低。不需要業務方感知和維護hashtag。

我們利用pipeline對Redis節點批量發送get命令,相對于Lettuce串行發送mget命令來說,減少了多次跨槽位mget發送命令的網絡耗時。具體步驟如下:

1、把所有key按照所在的Redis節點拆分;

2、通過pipeline對每個Redis節點批量發送get命令;

3、獲取所有命令執行結果,排序、合并結果,并返回。

這樣改造,使用pipeline一次發送批量的命令,減少了串行批量發送命令的網絡耗時。

3.2.1 改造JedisCluster

由于Lettuce沒有原生支持pipeline批量提交命令,而JedisCluster原生支持pipeline,并且JedisCluster沒有對Multi-Key進行支持,我們對JedisCluster的mget進行了改造,代碼如下:

public List<String> mget(String... keys) {
        List<Pipeline> pipelineList = new ArrayList<>();
        List<Jedis> jedisList = new ArrayList<>();
        try {
            //按照key的hash計算key位于哪一個redis節點
            Map<JedisPool, List<String>> pooling = new HashMap<>();
            for (String key : keys) {
                JedisPool pool = connectionHandler.getConnectionPoolFromSlot(JedisClusterCRC16.getSlot(key));
                pooling.computeIfAbsent(pool, k -> new ArrayList<>()).add(key);
            }
            //分別對每個redis 執行pipeline get操作
            Map<String, Response<String>> resultMap = new HashMap<>();
            for (Map.Entry<JedisPool, List<String>> entry : pooling.entrySet()) {
                Jedis jedis = entry.getKey().getResource();
                Pipeline pipelined = jedis.pipelined();
                for (String key : entry.getValue()) {
                    Response<String> response = pipelined.get(key);
                    resultMap.put(key, response);
                }
                pipelined.flush();
                //保存所有連接和pipeline 最后進行close
                pipelineList.add(pipelined);
                jedisList.add(jedis);
            }
            //同步所有請求結果
            for (Pipeline pipeline : pipelineList) {
                pipeline.returnAll();
            }
            //合并、排序結果
            List<String> list = new ArrayList<>();
            for (String key : keys) {
                Response<String> response = resultMap.get(key);
                String o = response.get();
                list.add(o);
            }
            return list;
        }finally {
            //關閉所有pipeline和jedis連接
            pipelineList.forEach(Pipeline::close);
            jedisList.forEach(Jedis::close);
        }
    }

3.2.2 處理異常case

上面的代碼還不足以覆蓋所有場景,我們還需要處理一些異常case

  • Redis Cluster擴縮容導致的數據遷移

數據遷移會造成兩種錯誤

1、MOVED錯誤

代表數據所在的槽位已經遷移到另一個redis節點上了,服務端會告訴客戶端對應的槽的目標節點信息。此時我們需要做的是更新客戶端緩存的槽位信息,并嘗試重新獲取數據。

2、ASKING錯誤

代表槽位正在遷移中,且數據不在源節點中,我們需要先向目標Redis節點執行ASKING命令,才能獲取遷移的槽位的數據。

List<String> list = new ArrayList<>();
for (String key : keys) {
    Response<String> response = resultMap.get(key);
    String o;
    try {
        o = response.get();
        list.add(o);
    } catch (JedisRedirectionException jre) {
        if (jre instanceof JedisMovedDataException) {
            //此槽位已經遷移 更新客戶端的槽位信息
            this.connectionHandler.renewSlotCache(null);
        }
        boolean asking = false;
        if (jre instanceof JedisAskDataException) {
            //獲取槽位目標redis節點的連接 設置asking標識,以便在重試前執行asking命令
            asking = true;
 askConnection.set(this.connectionHandler.getConnectionFromNode(jre.getTargetNode()));
        } else {
            throw new JedisClusterException(jre);
        }
        //重試獲取這個key的結果
        o = runWithRetries(this.maxAttempts, asking, true, key);
        list.add(o);
    }
}

數據遷移導致的兩種異常,會進行重試。重試會導致耗時增加,并且如果達到最大重試次數,還沒有獲取到數據,則拋出異常。

  • pipeline的某個命令執行失敗

不捕獲執行失敗的異常,拋出異常讓業務服務感知到異常發生。

四、 效果展示

4.1 性能測試

在改造完客戶端之后,我們對客戶端的mget進行了性能測試,測試了下面三種類型的耗時

1、使用Jedis訪問Codis

2、使用改造的JedisCluster訪問Redis Cluster

3、使用Lettuce同步方式訪問Redis Cluster

4.1.1 mget 100key


Codis

JedisCluster(改造)

Lettuce

avg

0.411ms

0.224ms

0.61ms

tp99

0.528ms

0.35ms

1.53ms

tp999

0.745ms

1.58ms

3.87ms

4.1.2 mget 500key


Codis

JedisCluster(改造)

Lettuce

avg

0.96ms

0.511ms

2.14ms

tp99

1.15ms

0.723ms

3.99ms

tp999

1.81ms

1.86ms

6.88ms

4.1.3 mget 1000key


Codis

JedisCluster(改造)

Lettuce

avg

1.56ms

0.92ms

5.04ms

tp99

1.83ms

1.22ms

8.91ms

tp999

3.15ms

3.88ms

32ms

4.2 結論

  • 使用改造的客戶端訪問Redis Cluster,比使用Lettuce訪問Redis Cluster要快1倍以上;
  • 改造的客戶端比使用codis稍微快一點,tp999不如codis性能好。

但是改造的客戶端相對于Lettuce也有缺點,JedisCluster是基于復雜的連接池實現,連接池的配置會影響客戶端的性能。而Lettuce是基于Netty的NIO框架實現,對于大多數的Redis操作,只需要維持單一的連接即可高效支持并發請求,不需要業務考慮連接池的配置。

五、 總結

Redis Cluster在架構設計上對Multi-Key進行的限制,導致無法跨槽位執行mget等命令。我們對客戶端JedisCluster的Multi-Key命令進行改造,通過分別對Redis節點執行pipeline操作,提升了mget命令的性能。

責任編輯:龐桂玉 來源: 轉轉技術
相關推薦

2023-02-16 08:00:00

數據流客戶端開發數據集

2009-12-07 18:26:36

WCF客戶端

2011-08-15 14:09:59

JavaHBase

2009-06-12 19:18:08

REST客戶端框架JavaScript

2011-04-22 10:34:09

SimpleFrame

2022-03-19 12:16:49

Redis高并發系統集群部署

2009-03-18 14:44:34

LinuxqTwitterTwitter

2021-09-22 15:46:29

虛擬桌面瘦客戶端胖客戶端

2025-01-07 08:10:00

CefSharpWinformWindows

2022-04-15 10:52:50

模型技術實踐

2025-11-28 04:00:00

RedisClusterSharding

2022-09-15 16:48:30

MySQL數據庫測試

2021-08-01 23:18:21

Redis Golang命令

2011-03-21 14:53:36

Nagios監控Linux

2011-04-06 14:24:20

Nagios監控Linux

2011-08-17 10:10:59

2009-06-23 14:00:49

JavaFX開發

2024-09-11 09:50:22

2021-06-22 15:06:13

Redis客戶端 Redis-clie

2020-05-11 21:31:02

Redis 6.0緩存客戶端
點贊
收藏

51CTO技術棧公眾號

国产精品久线观看视频| 欧美日韩在线观看首页| 久久在线播放| 亚洲国产精品高清久久久| 国产精品无码av在线播放| 亚洲第一偷拍| 精品中文字幕乱| 国产激情在线| 亚洲人成小说网站色在线 | 乱中年女人伦av一区二区| 91精品国产色综合久久不卡蜜臀 | 欧美丝袜丝nylons| 久热精品在线观看视频| 国产在线看一区| 国产精品我不卡| 国产欧美日韩免费观看 | 中文字幕免费一区二区三区| 欧美巨猛xxxx猛交黑人97人| 主播国产精品| 丰满岳妇乱一区二区三区| www.玖玖玖| 国产综合久久久久久鬼色| 国产高清自拍一区| 精品日韩免费| 97在线日本国产| 久久91视频| 亚洲欧美在线免费| 免费不卡av| 欧美日韩国产大片| 91网在线观看| 日韩一区欧美一区| 日韩 欧美 高清| 99久免费精品视频在线观看 | 韩剧1988在线观看免费完整版| 小视频免费在线观看| 欧美日韩在线三区| 小水嫩精品福利视频导航| 一区二区三区欧美视频| 91在线网站| 国产精品主播直播| 全黄性性激高免费视频| 久久久久国产一区二区三区四区| 动漫成人在线| 亚洲国产精品嫩草影院久久| 亚洲伦理一区二区| 亚洲精品欧美极品| 久久久青草青青国产亚洲免观| 国产爆乳无码一区二区麻豆| 欧美激情精品| 亚洲色图清纯唯美| 黄色大秀av大片| 国产清纯在线一区二区www| 日产中文字幕在线精品一区| 高清在线视频不卡| 亚洲伦伦在线| 日韩av电影在线播放| 亚洲精品承认| 狠狠干综合网| 亚洲午夜av电影| 青青艹视频在线| 草美女在线观看| 午夜a成v人精品| 男人天堂成人网| 91精品亚洲| 91国内精品野花午夜精品| 在线观看黄网| 91福利国产成人精品照片| 在线视频1区2区| 日韩欧美专区在线| 欧美色网一区| 欧美美女操人视频| 欧美天天综合| 国产一区二区三区四区hd | 亚洲综合一区二区三区| 中文字幕免费在线观看| 欧美色大人视频| 一区二区三区电影大全| 久久91精品国产| 第一会所sis001亚洲| 久久伊人资源站| 懂色av一区二区夜夜嗨| 男女午夜刺激视频| 欧美在线免费视屏| 成人看片网站| 97精品欧美一区二区三区| 一区二区影视| 台湾无码一区二区| 亚洲国产精品久久久男人的天堂| 中文字幕伦理免费在线视频| 久久99精品久久久久久噜噜 | 99久久这里有精品| 国产精品毛片a∨一区二区三区|国 | 日韩片电影在线免费观看| aaa亚洲精品| 色鬼7777久久| 亚洲欧美激情在线视频| 免费av一区| 久久久一本精品99久久精品| 2020国产精品久久精品美国| 欧美欧美欧美| 色青青草原桃花久久综合| 成人免费看片39| 在线观看18视频网站| 一区二区三区日本| yellow在线观看网址| 国产极品jizzhd欧美| 精品一区二区三区的国产在线播放| 黑森林精品导航| 日韩一级二级三级| 综合国产视频| 日本一区午夜艳熟免费| 欧美影院一区二区| 国产劲爆久久| 亚洲欧洲一区二区福利| 亚洲一本大道在线| 激情亚洲小说| 欧美日韩精品久久久免费观看| 久久精品一二三| av色在线观看| 97超碰人人看人人| 国产精品丝袜黑色高跟| 阿v视频在线观看| 波多野结衣一区二区三区在线观看| 26uuu久久综合| 欧美一卡二卡| 91中文字幕一区| 国产精品福利一区| 羞羞影院欧美| 免费精品视频一区二区三区| 亚洲一区二区三区四区在线免费观看| 欧洲av一区二区| 少妇精品久久久久久久久久| 性做久久久久久免费观看欧美| 日韩在线观看中文字幕| 国产又黄又爽免费视频| 日韩午夜激情电影| 国内视频精品| 伊人久久青青草| 国产69久久精品成人| 91免费视频网址| av在线日韩| a级网站在线观看| 精品国产乱码久久久久久老虎 | 日本不卡在线视频| 天堂a√在线| 国产精品91在线| 国产视频不卡一区| 伊人亚洲精品| 黄色免费视频大全| 在线视频中文亚洲| 国产精品99久久久久久久vr| 天堂中文在线播放| 伊人狠狠色丁香综合尤物| 日韩欧美在线综合网| 香蕉久久夜色精品国产| xvideos国产在线视频| 国产伦精品一区二区三区照片91 | 国产99久久精品一区二区300| 免费观看精品视频| 日韩一区二区福利| 91亚洲精品久久久蜜桃网站 | 成人写真视频福利网| 一区二区三区在线视频观看| 日韩在线成人| 97影视在线观看| 国产成人小视频在线观看| 亚洲一区二区欧美激情| 国产乱码精品一区二区三区四区| 国产精品666| 国产精品盗摄久久久| 亚洲一区免费视频| 在线中文一区| 操你啦视频在线| 中文字幕人成一区| 丝袜亚洲欧美日韩综合| 欧美激情中文不卡| 视频一区中文| 四虎国产精品永远| 国产精品一码二码三码在线| 91精品国产一区二区| 紧缚捆绑精品一区二区| 成人全视频免费观看在线看| 欧美性大战久久久久xxx| 欧美精品激情在线观看| 亚洲精品视频在线| 91亚洲国产高清| 91在线不卡| 日韩视频 中文字幕| 欧美多人爱爱视频网站| 亚洲一区二区五区| 亚洲美女啪啪| 成人一区福利| 最近久乱中文字幕| 国产精品久久久久av福利动漫| 亚洲二区在线播放视频| 99久久精品国产精品久久| 精品免费在线| 超碰91在线观看| 国产精品久久久毛片| 亚洲va欧美va国产综合剧情|