0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

緩存有大key?你得知道的一些手段

京東云 ? 來(lái)源:jf_75140285 ? 作者:jf_75140285 ? 2024-06-19 09:38 ? 次閱讀

wKgaomZxZm6AdTxjAAT8AKxapPY680.png

??

?

背景:

最近系統(tǒng)內(nèi)緩存CPU使用率一直報(bào)警,超過(guò)設(shè)置的70%報(bào)警閥值,針對(duì)此場(chǎng)景,需要對(duì)應(yīng)解決緩存是否有大key使用問(wèn)題,掃描緩存集群的大key,針對(duì)每個(gè)key做優(yōu)化處理。

以下是掃描出來(lái)的大key,此處只放置了有效關(guān)鍵信息。

wKgZomZxZnCAYdWPAAEdxfwBKtA163.png

??

圖1

大key介紹:

想要解決大key,首先我們得知道什么定義為大key。

什么是大KEY:

大key 并不是指 key 的值很大,而是 key 對(duì)應(yīng)的 value 很大(非常占內(nèi)存)。此處為中間件給出的定義:

?單個(gè)String類型的Key大小達(dá)到20KB并且OPS高

?單個(gè)String達(dá)到100KB

?集合類型的Key總大小達(dá)到1MB

?集合類型的Key中元素超過(guò)5000個(gè)

大KEY帶來(lái)的影響:

知道了大key的定義,那么我們也得知道大key的帶來(lái)的影響:

?客戶端超時(shí)阻塞。 Redis 執(zhí)行命令是單線程處理,然后在大 key處理時(shí)會(huì)比較耗時(shí),那么就會(huì)發(fā)生阻塞 ,期間就會(huì)各種業(yè)務(wù)超時(shí)出現(xiàn)。

?引發(fā)網(wǎng)絡(luò)阻塞。每次獲取大 key 產(chǎn)生的網(wǎng)絡(luò)流量較大,如果一個(gè) key 的大小是 1 MB,每秒訪問(wèn)量為 1000,那么每秒會(huì)產(chǎn)生 1000MB 的流量,這對(duì)于服務(wù)器來(lái)說(shuō)是災(zāi)難性的。

?阻塞工作線程。如果使用 del 刪除大 key 時(shí),會(huì)阻塞工作線程,無(wú)法處理后續(xù)的命令。

?內(nèi)存分布不均。集群各分片內(nèi)存使用不均。某個(gè)分片占用內(nèi)存較高OOM,發(fā)送緩存區(qū)增大等,導(dǎo)致該分片其他Key被逐出,同時(shí)也會(huì)造成其他分片的資源浪費(fèi)。

大KEY解決手段:

1、歷史key未使用

場(chǎng)景描述:

針對(duì)這種key場(chǎng)景,其實(shí)存在著歷史原因,可能是伴隨著某個(gè)業(yè)務(wù)下線或者不使用,往往對(duì)應(yīng)實(shí)現(xiàn)的緩存操作代碼會(huì)刪除,但是對(duì)于緩存數(shù)據(jù)往往不會(huì)做任何處理,久而久之,這種臟數(shù)據(jù)會(huì)一直堆積,占用著資源。那么如果確定已經(jīng)無(wú)使用,并且可以確認(rèn)有持久化數(shù)據(jù)(如mysql、es等)備份的話,可以直接將對(duì)應(yīng)key刪除。

實(shí)例經(jīng)驗(yàn):

如圖1上面的元素個(gè)數(shù)488649,其實(shí)整個(gè)系統(tǒng)查看了下,沒(méi)有使用的地方,最近也沒(méi)有訪問(wèn),相信也是因?yàn)橐恢睕](méi)有用到, 否則系統(tǒng)內(nèi)一旦用了這個(gè)key來(lái)操作hgetall、smembers等,那么緩存服務(wù)應(yīng)該就會(huì)不可用了。

2、元素?cái)?shù)過(guò)多

場(chǎng)景描述:

針對(duì)于Set、HASH這種場(chǎng)景,如果元素?cái)?shù)量超過(guò)5000就視為大的key,以上面圖1為例,可以看到元素個(gè)數(shù)有的甚至達(dá)到了1萬(wàn)以上。針對(duì)這種的如果對(duì)應(yīng)value值不大,我們可以采取平鋪的形式,

實(shí)例經(jīng)驗(yàn):

比如系統(tǒng)內(nèi)歷史的設(shè)計(jì)是存儲(chǔ)下每個(gè)品牌對(duì)應(yīng)的名稱,那么就設(shè)置了統(tǒng)一的key,然后不同的品牌id作為fild,操作了hSet和hGet來(lái)存儲(chǔ)獲取數(shù)據(jù),降低查詢外圍服務(wù)的頻率。但是隨著品牌數(shù)量的增長(zhǎng),導(dǎo)致元素逐步增多,元素個(gè)數(shù)就超過(guò)了大key的預(yù)設(shè)值了。這種根據(jù)場(chǎng)景,我們其實(shí)存儲(chǔ)本身只有一個(gè)品牌名稱,那么我們就針對(duì)于品牌id對(duì)應(yīng)加上一個(gè)統(tǒng)一前綴作為唯一key,采用平鋪方式緩存對(duì)應(yīng)數(shù)據(jù)即可。那么針對(duì)這種數(shù)據(jù)的替換,我這里也總結(jié)了下具體要實(shí)現(xiàn)的步驟:

修改代碼查詢和賦值邏輯:

?把原始的hGet的邏輯修改為get獲取;

?把原始hSet的邏輯修改為set賦值。

歷史數(shù)據(jù)刷新到新緩存key:

為了避免上線之后出現(xiàn)緩存雪崩,因?yàn)樘鎿Q了新的key,我們需要通過(guò)現(xiàn)有的HASH的數(shù)據(jù)刷新到新的緩存中,所以需要?dú)v史數(shù)據(jù)處理。

通過(guò)hGetAll獲取所以元素?cái)?shù)據(jù)

循環(huán)緩存元素?cái)?shù)據(jù)操作存儲(chǔ)新的緩存key和value。

public String refreshHistoryData(){
    try {
        String key = "historyKey";
        Map redisInfoMap= redisUtils.hGetAll(key);
        if (redisInfoMap.isEmpty()){
            return "查詢緩存無(wú)數(shù)據(jù)";
        }
        for (Map.Entry entry : redisInfoMap.entrySet()) {
            String redisVal = entry.getValue();
            String filedKey = entry.getKey();
            String newDataRedisKey = "newDataKey"+filedKey;
            redisUtils.set(newDataRedisKey,redisVal);
        }
        return "success";
    }catch (Exception e){
        LOG.error("refreshHistoryData 異常:",e);
    }
    return "failed";
}

注意:這里一定要先刷歷史數(shù)據(jù),再上線代碼業(yè)務(wù)邏輯的修改。防止引發(fā)緩存雪崩

3、大對(duì)象轉(zhuǎn)換存儲(chǔ)形式

場(chǎng)景描述:

復(fù)雜的大對(duì)象可以嘗試將對(duì)象分拆成幾個(gè)key-value, 使用mGet和mSet操作對(duì)應(yīng)值或者pipeline的形式,最后拼裝成需要返回的大對(duì)象。這樣意義在于可以分散單次操作的壓力,將操作壓力平攤到多個(gè)redis實(shí)例中,降低對(duì)單個(gè)redis的IO影響;

實(shí)例經(jīng)驗(yàn):

這里以系統(tǒng)內(nèi)訂單對(duì)象為例:訂單對(duì)象Order基礎(chǔ)屬性有幾十個(gè),如訂單號(hào)、金額、時(shí)間、類型等,除此之外還要包含訂單下的商品OrderSub、預(yù)售信息PresaleOrder、發(fā)票信息OrderInvoice、訂單時(shí)效OrderPremiseInfo、訂單軌跡OrderTrackInfo、訂單詳細(xì)費(fèi)用OrderFee等信息。

那么對(duì)于每個(gè)訂單相關(guān)信息,我們可以設(shè)置為單獨(dú)的key,把訂單信息和幾個(gè)相關(guān)的關(guān)聯(lián)數(shù)據(jù)每個(gè)按照單獨(dú)key存儲(chǔ),接著通過(guò)mGet方式獲取每個(gè)信息之后,最后封裝成整體Order對(duì)象。下面僅展示關(guān)鍵偽代碼以mSet和mGet實(shí)現(xiàn):

緩存定義:

public enum CacheKeyConstant {

    /**
     * 訂單基礎(chǔ)緩存key
     */
    REDIS_ORDER_BASE_INFO("ORDER_BASE_INFO"),

    /**
     * 訂單商品緩存key
     */
    ORDER_SUB_INFO("ORDER_SUB_INFO"),

    /**
     * 訂單預(yù)售信息緩存key
     */
    ORDER_PRESALE_INFO("ORDER_PRESALE_INFO"),

    /**
     * 訂單履約信息緩存key
     */
    ORDER_PREMISE_INFO("ORDER_PREMISE_INFO"),

    /**
     * 訂單發(fā)票信息緩存key
     */
    ORDER_INVOICE_INFO("ORDER_INVOICE_INFO"),

    /**
     * 訂單軌跡信息緩存key
     */
    ORDER_TRACK_INFO("ORDER_TRACK_INFO"),

    /**
     * 訂單詳細(xì)費(fèi)用信息緩存key
     */
    ORDER_FEE_INFO("ORDER_FEE_INFO"),
    ;
    /**
     * 前綴
     */
    private String prefix;

    /**
     * 項(xiàng)目統(tǒng)一前綴
     */
    public static final String COMMON_PREFIX = "XXX";


    CacheKeyConstant(String prefix){
        this.prefix = prefix;
    }

    public String getPrefix(String subKey) {
        if(StringUtil.isNotEmpty(subKey)){
            return COMMON_PREFIX + prefix + "_" + subKey;
        }
        return COMMON_PREFIX + prefix;
    }

    public String getPrefix() {
        return COMMON_PREFIX + prefix;
    }
}

緩存存儲(chǔ):

/**
 * @description 刷新訂單到緩存
 * @param order 訂單信息
 */
public boolean refreshOrderToCache(Order order){
     if(order == null || order.getOrderId() == null){
        return ;
    }
    String orderId = order.getOrderId().toString();
    //設(shè)置存儲(chǔ)緩存數(shù)據(jù)
    Map cacheOrderMap = new HashMap(16);
    cacheOrderMap.put(CacheKeyConstant.ORDER_BASE_INFO.getPrefix(orderId), JSON.toJSONString(buildBaseOrderVo(order)));
    cacheOrderMap.put(CacheKeyConstant.ORDER_SUB_INFO.getPrefix(orderId), JSON.toJSONString(order.getCustomerOrderSubs()));
    cacheOrderMap.put(CacheKeyConstant.ORDER_PRESALE_INFO.getPrefix(orderId), JSON.toJSONString(order.getPresaleOrderData()));
    cacheOrderMap.put(CacheKeyConstant.ORDER_INVOICE_INFO.getPrefix(orderId), JSON.toJSONString(order.getOrderInvoice()));
    cacheOrderMap.put(CacheKeyConstant.ORDER_TRACK_INFO.getPrefix(orderId), JSON.toJSONString(order.getOrderTrackInfo()));
    cacheOrderMap.put(CacheKeyConstant.ORDER_PREMISE_INFO.getPrefix(orderId), JSON.toJSONString( order.getPresaleOrderData()));
    cacheOrderMap.put(CacheKeyConstant.ORDER_FEE_INFO.getPrefix(orderId), JSON.toJSONString(order.getOrderFeeVo()));
    superRedisUtils.mSetString(cacheOrderMap);
}

緩存獲?。?/p>

/**
 * @description 通過(guò)訂單號(hào)獲取緩存數(shù)據(jù)
 * @param orderId 訂單號(hào)
 * @return Order 訂單實(shí)體信息
 */
public Order getOrderFromCache(String orderId){
    if(StringUtils.isBlank(orderId)){
            return null;
    }
    //定義查詢緩存集合key
    List queryOrderKey = Arrays.asList(CacheKeyConstant.ORDER_BASE_INFO.getPrefix(orderId),CacheKeyConstant.ORDER_SUB_INFO.getPrefix(orderId),
            CacheKeyConstant.ORDER_PRESALE_INFO.getPrefix(orderId),CacheKeyConstant.ORDER_INVOICE_INFO.getPrefix(orderId),CacheKeyConstant.ORDER_TRACK_INFO.getPrefix(orderId),
            CacheKeyConstant.ORDER_PREMISE_INFO.getPrefix(orderId),CacheKeyConstant.ORDER_FEE_INFO.getPrefix(orderId));

    //查詢結(jié)果
    List result = redisUtils.mGet(queryOrderKey);
    //基礎(chǔ)信息
    if(CollectionUtils.isEmpty(result)){
        return null;
    }
    String[] resultInfo = result.toArray(new String[0]);

    //基礎(chǔ)信息
    if(StringUtils.isBlank(resultInfo[0])){
        return null;
    }
    BaseOrderVo baseOrderVo = JSON.parseObject(resultInfo[0],BaseOrderVo.class);
    Order order = coverBaseOrderVoToOrder(baseOrderVo);

    //訂單商品
    if(StringUtils.isNotBlank(resultInfo[1])){
        List orderSubs =JSON.parseObject(result.get(1), new TypeReference>(){});
        order.setCustomerOrderSubs(orderSubs);
    }
    //訂單預(yù)售
    if(StringUtils.isNotBlank(resultInfo[2])){
        PresaleOrderData presaleOrderData = JSON.parseObject(resultInfo[2],PresaleOrderData.class);
        order.setPresaleOrderData(presaleOrderData);
    }
    //訂單發(fā)票
    if(StringUtils.isNotBlank(resultInfo[3])){
        OrderInvoice orderInvoice = JSON.parseObject(resultInfo[3],OrderInvoice.class);
        order.setOrderInvoice(orderInvoice);
    }
    //訂單軌跡
    if(StringUtils.isNotBlank(resultInfo[5])){
        OrderTrackInfo orderTrackInfo = JSON.parseObject(resultInfo[5],OrderTrackInfo.class);
        order.setOrderTrackInfo(orderTrackInfo);
    }
    //訂單履約信息
    if(StringUtils.isNotBlank(resultInfo[6])){
        List orderPremiseInfos =JSON.parseObject(result.get(6), new TypeReference>(){});
        order.setPremiseInfos(orderPremiseInfos);
    }
    //訂單費(fèi)用明細(xì)信息
    if(StringUtils.isNotBlank(resultInfo[7])){
        OrderFeeVo orderFeeVo = JSON.parseObject(resultInfo[7],OrderFeeVo.class);
        order.setOrderFeeVo(orderFeeVo);
    }
    return order;
}

注意:獲取緩存的結(jié)果跟傳入的key的順序保持對(duì)應(yīng)即可。

緩存util方法封裝:

/**
 *
 * @description 同時(shí)將多個(gè) key-value (域-值)對(duì)設(shè)置到緩存中。
 * @param mappings 需要插入的數(shù)據(jù)信息
 */
public void mSetString(Map mappings) {
    CallerInfo callerInfo = Ump.methodReg(UmpKeyConstants.REDIS.REDIS_STATUS_READ_MSET);
    try {
        redisClient.getClientInstance().mSetString(mappings);
    } catch (Exception e) {
        Ump.funcError(callerInfo);
    }finally {
        Ump.methodRegEnd(callerInfo);
    }
}
/**
 *
 * @description 同時(shí)將多個(gè)key的結(jié)果返回。
 * @param queryKeys 查詢的緩存key集合
 */
public List mGet(List queryKeys) {
    CallerInfo callerInfo = Ump.methodReg(UmpKeyConstants.REDIS.REDIS_STATUS_READ_MGET);
    try {
        return redisClient.getClientInstance().mGet(queryKeys.toArray(new String[0]));
    } catch (Exception e) {
        Ump.funcError(callerInfo);
    }finally {
        Ump.methodRegEnd(callerInfo);
    }
    return new ArrayList(queryKeys.size());
}

這里附上通過(guò)pipeline的util封裝,可參考。

/**
 * @description pipeline放松查詢數(shù)據(jù)
 * @param redisKeyList
 * @return java.util.List
 */
public List getValueByPipeline(List redisKeyList) {
        if(CollectionUtils.isEmpty(redisKeyList)){
            return null;
        }
        List resultInfo = new ArrayList(redisKeyList);
        CallerInfo callerInfo = Ump.methodReg(UmpKeyConstants.REDIS.REDIS_STATUS_READ_GET);
        try {

            PipelineClient pipelineClient = redisClient.getClientInstance().pipelineClient();

            //添加批量查詢?nèi)蝿?wù)
            List futures = new ArrayList();
            redisKeyList.forEach(redisKey -> {
                futures.add(pipelineClient.get(redisKey.getBytes()));
            });
            //處理查詢結(jié)果
            pipelineClient.flush();
            //可以等待future的返回結(jié)果,來(lái)判斷命令是否成功。
            for (JimFuture future : futures) {
                resultInfo.add(new String((byte[])future.get()));
            }

        } catch (Exception e) {
            log.error("getValueByPipeline error:",e);
            Ump.funcError(callerInfo);
            return new ArrayList(redisKeyList.size());
        }finally {
            Ump.methodRegEnd(callerInfo);
        }
        return resultInfo;
    }

注意:Pipeline不建議用來(lái)設(shè)置緩存值,因?yàn)楸旧聿皇窃有缘牟僮鳌?/p>

4、壓縮存儲(chǔ)數(shù)據(jù)

壓縮方法結(jié)果:

單個(gè)元素時(shí):

wKgaomZxZnGAEV6-AAGqLX4gDK0727.png

??

壓縮方法 壓縮前大小Byte 壓縮后大小Byte 壓縮耗時(shí) 解壓耗時(shí) 壓縮解壓后比對(duì)結(jié)果
DefaultOutputStream 446(0.43kb) 254 (0.25kb) 1ms 0ms 相同
GzipOutputStream 446(0.43kb) 266 (0.25kbM) 1ms 1ms 相同
ZlibCompress 446(0.43kb) 254 (0.25kb) 1ms 0ms 相同

四百個(gè)元素集合:

wKgaomZxZnOAQinXAAGxes81m5o547.png

??

壓縮方法 壓縮前大小Byte 壓縮后大小Byte 壓縮耗時(shí) 解壓耗時(shí) 壓縮解壓后比對(duì)結(jié)果
DefaultOutputStream 6732(6.57kb) 190 (0.18kb) 2ms 0ms 相同
GzipOutputStream 6732(6.57kb) 202 (0.19kb) 1ms 1ms 相同
ZlibCompress 6732(6.57kb) 190 (0.18kb) 1ms 0ms 相同

四萬(wàn)個(gè)元素集合時(shí):

wKgZomZxZnSAY1d0AAG3-0egN1E960.png

??

壓縮方法 壓縮前大小Byte 壓縮后大小Byte 壓縮耗時(shí) 解壓耗時(shí) 壓縮解壓后比對(duì)結(jié)果
DefaultOutputStream 640340(625kb) 1732 (1.69kb) 37ms 2ms 相同
GzipOutputStream 640340(625kb) 1744 (1.70kb) 11ms 3ms 相同
ZlibCompress 640340(625kb) 1732 (1.69kb) 69ms 2ms 相同

壓縮代碼樣例

DefaultOutputStream

public static byte[] compressToByteArray(String text) throws IOException {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    Deflater deflater = new Deflater();
    DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(outputStream, deflater);

    deflaterOutputStream.write(text.getBytes());
    deflaterOutputStream.close();

    return outputStream.toByteArray();
}

public static String decompressFromByteArray(byte[] bytes) throws IOException {
    ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
    Inflater inflater = new Inflater();
    InflaterInputStream inflaterInputStream = new InflaterInputStream(inputStream, inflater);
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

    byte[] buffer = new byte[1024];
    int length;
    while ((length = inflaterInputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, length);
    }

    inflaterInputStream.close();
    outputStream.close();

    byte[] decompressedData = outputStream.toByteArray();
    return new String(decompressedData);
}

GZIPOutputStream

public static byte[] compressGzip(String str) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        GZIPOutputStream gzipOutputStream = null;
        try {
            gzipOutputStream = new GZIPOutputStream(outputStream);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        try {
            gzipOutputStream.write(str.getBytes("UTF-8"));
        } catch (IOException e) {
            throw new RuntimeException(e);
        }finally {
            try {
                gzipOutputStream.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return outputStream.toByteArray();
    }

 public static String decompressGzip(byte[] compressed) throws IOException {
        ByteArrayInputStream inputStream = new ByteArrayInputStream(compressed);
        GZIPInputStream gzipInputStream = new GZIPInputStream(inputStream);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int length;
        while ((length = gzipInputStream.read(buffer)) > 0) {
            outputStream.write(buffer, 0, length);
        }
        gzipInputStream.close();
        outputStream.close();
        return outputStream.toString("UTF-8");
    }

ZlibCompress

 public  byte[] zlibCompress(String message) throws Exception {
        String chatacter = "UTF-8";
        byte[] input = message.getBytes(chatacter);
        BigDecimal bigDecimal = BigDecimal.valueOf(0.25f);
        BigDecimal length = BigDecimal.valueOf(input.length);
        byte[] output = new byte[input.length + 10 + new Double(Math.ceil(Double.parseDouble(bigDecimal.multiply(length).toString()))).intValue()];
        Deflater compresser = new Deflater();
        compresser.setInput(input);
        compresser.finish();
        int compressedDataLength = compresser.deflate(output);
        compresser.end();
        return Arrays.copyOf(output, compressedDataLength);
    }

public static String zlibInfCompress(byte[] data) {
        String s = null;

        Inflater decompresser = new Inflater();
        decompresser.reset();
        decompresser.setInput(data);
        ByteArrayOutputStream o = new ByteArrayOutputStream(data.length);
        try {
            byte[] buf = new byte[1024];
            while (!decompresser.finished()) {
                int i = decompresser.inflate(buf);
                o.write(buf, 0, i);
            }
            s = o.toString("UTF-8");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                o.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        decompresser.end();
        return s;
    }

可以看到壓縮效率比較好,壓縮效率可以從幾百kb壓縮到幾kb內(nèi);當(dāng)然也是看具體場(chǎng)景。不過(guò)這里就是最好是避免調(diào)用量大的場(chǎng)景使用,畢竟解壓和壓縮數(shù)據(jù)量大會(huì)比較耗費(fèi)cpu性能。如果是黃金鏈路使用,還需要具體配合壓測(cè),對(duì)比前后接口性能。

5、替換存儲(chǔ)方案

如果數(shù)據(jù)量龐大,那么其實(shí)本身是不是就不太適合redis這種緩存存儲(chǔ)了??梢钥紤]es或者mongo這種文檔式存儲(chǔ)結(jié)構(gòu),存儲(chǔ)大的數(shù)據(jù)格式。

總結(jié):

redis緩存的使用是一個(gè)支持業(yè)務(wù)和功能高并發(fā)的很好的使用方案,但是隨著使用場(chǎng)景的多樣性以及數(shù)據(jù)的增加,可能逐漸的會(huì)出現(xiàn)大key,日常使用中都可以注意以下幾點(diǎn):

1.分而治之:如果需要存儲(chǔ)大量的數(shù)據(jù),避免直接放到緩存中??梢詫⑵洳鸱殖啥鄠€(gè)小的value。就像是咱們?nèi)粘3燥垼⒌酵肜?,一口一口的吃,俗話說(shuō)的好呀:“細(xì)嚼慢咽”。

2.避免使用不必要的數(shù)據(jù)結(jié)構(gòu)。例如,如果只需要存儲(chǔ)一個(gè)字符串結(jié)構(gòu)的數(shù)據(jù),就不要過(guò)度設(shè)計(jì),使用Hash或者List等數(shù)據(jù)結(jié)構(gòu)。

3.定期清理過(guò)期的key。如果Redis中存在大量的過(guò)期key,就會(huì)導(dǎo)致Redis的性能下降,或者場(chǎng)景非必要以緩存來(lái)持久存儲(chǔ)的,可以添加過(guò)期時(shí)間,定時(shí)清理過(guò)期的key,就像是家中的日常垃圾類似,定期的清潔和打掃,居住起來(lái)咱們才會(huì)更加舒服和方便。

4.對(duì)象壓縮。將大的數(shù)據(jù)壓縮成更小的數(shù)據(jù),也是一種好的解決方案,不過(guò)要注意壓縮和解壓的頻率,畢竟是比較耗費(fèi)cpu的。

以上是我根據(jù)現(xiàn)有實(shí)際場(chǎng)景總結(jié)出的一些解決手段,記錄了這些大key的優(yōu)化經(jīng)驗(yàn),希望可以在日常場(chǎng)景中幫助到大家。大家有其他的好的經(jīng)驗(yàn),也可以分享出來(lái)。

審核編輯 黃宇

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 緩存
    +關(guān)注

    關(guān)注

    1

    文章

    228

    瀏覽量

    26610
  • key
    key
    +關(guān)注

    關(guān)注

    0

    文章

    47

    瀏覽量

    12803
  • Redis
    +關(guān)注

    關(guān)注

    0

    文章

    370

    瀏覽量

    10811
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    ESP32-C3使用SPI通信緩存有多少?

    我們計(jì)劃用ESP32-AT功能,看到資料上ESP32-C3僅支持SPI通信,請(qǐng)問(wèn): ①ESP32-C3使用SPI通信緩存有多少? ②ESP32-C3使用SPI通信接收到數(shù)據(jù)以后空中速率可以達(dá)到
    發(fā)表于 06-26 07:58

    ZigBee天線接收緩存有多大的空間?

    1、ZigBee天線接收緩存有多大的空間?2、如果多對(duì)發(fā)送,接收方是否存在同時(shí)接收沖突而造成丟包,協(xié)議棧是否有處理?
    發(fā)表于 03-16 11:03

    得知道為什么要用Cortex-M

    得知道為什么要用Cortex-M無(wú)論您是資深嵌入式工程師,還是懷著滿滿好奇的嵌入式小白,總會(huì)有這樣的一些疑惑:Cortx-A、R、M到底什么區(qū)別呢?M0、M0+、M3、M4又有什
    發(fā)表于 06-09 16:50

    渲染中的幀緩存和深度緩存

    渲染涉及大量的緩存,這里緩存只是個(gè)簡(jiǎn)單的存有像素?cái)?shù)據(jù)的矩形內(nèi)存塊,最重要緩存是幀緩存和深度
    的頭像 發(fā)表于 05-14 11:44 ?6244次閱讀
    渲染中的幀<b class='flag-5'>緩存</b>和深度<b class='flag-5'>緩存</b>

    硬盤(pán)緩存有什么用

    硬盤(pán)緩存般指高速緩沖存儲(chǔ)器。本視頻主要詳細(xì)介紹了硬盤(pán)緩存有什么用,分別有預(yù)讀取、是對(duì)寫(xiě)入動(dòng)作進(jìn)行緩存以及是臨時(shí)存儲(chǔ)最近訪問(wèn)過(guò)的數(shù)據(jù)。
    的頭像 發(fā)表于 11-10 10:31 ?2.2w次閱讀

    處理器緩存有啥用

    按照數(shù)據(jù)讀取順序和與CPU結(jié)合的緊密程度,CPU緩存可以分為級(jí)緩存,二級(jí)緩存,如今主流CPU還有三級(jí)緩存,甚至有些CPU還有四級(jí)
    的頭像 發(fā)表于 01-21 17:11 ?1.5w次閱讀

    區(qū)塊鏈的事實(shí)知道一些

    比特幣水龍頭是個(gè)獎(jiǎng)勵(lì)系統(tǒng),主要出現(xiàn)在一些網(wǎng)站或應(yīng)用中,以satoshi(百萬(wàn)分之比特幣轉(zhuǎn)換比特幣)的形式發(fā)放獎(jiǎng)勵(lì),訪客可以通過(guò)完成網(wǎng)站描述的驗(yàn)證碼或任務(wù)來(lái)獲得獎(jiǎng)勵(lì)。
    發(fā)表于 09-11 09:38 ?837次閱讀

    安全工程師的這些事得知道

    從領(lǐng)證,到注冊(cè),再到執(zhí)業(yè)!來(lái)自靈魂的質(zhì)問(wèn)三連擊,考過(guò)了注冊(cè)安全工程師,這些事兒得知道!
    的頭像 發(fā)表于 10-10 16:58 ?3130次閱讀

    CPU緩存是什么意思_CPU緩存有什么作用

    由于處理器是核心硬件,相信我們?cè)谶x擇處理器的時(shí)候都會(huì)去關(guān)心處理器參數(shù)方面,而在處理器核心參數(shù)中,我們經(jīng)常會(huì)看到緩存(Cache)這個(gè)參數(shù),那么CPU的緩存有什么作用呢?下面小編科普下關(guān)于CPU
    發(fā)表于 05-19 09:24 ?7361次閱讀

    知道開(kāi)關(guān)電源布局以及印制板布線的一些原則嗎

    在生活中,可能接觸過(guò)各種各樣的電子產(chǎn)品,那么可能并不知道它的一些組成部分,比如它可能含有的開(kāi)關(guān)電源,那么接下來(lái)讓小編帶領(lǐng)大家起學(xué)習(xí)開(kāi)關(guān)
    發(fā)表于 03-17 19:12 ?27次下載
    <b class='flag-5'>你</b><b class='flag-5'>知道</b>開(kāi)關(guān)電源布局以及印制板布線的<b class='flag-5'>一些</b>原則嗎

    SpinalHDL里用于跨時(shí)鐘域處理的一些手段方法

    個(gè)做數(shù)字邏輯的都繞不開(kāi)跨時(shí)鐘域處理,談談SpinalHDL里用于跨時(shí)鐘域處理的一些手段方法。
    的頭像 發(fā)表于 07-11 10:51 ?1734次閱讀

    一些也許您還不知道的 TINA-TI 某些資源! (IV)

    一些也許您還不知道的 TINA-TI 某些資源! (IV)
    發(fā)表于 11-04 09:52 ?4次下載
    <b class='flag-5'>一些</b>也許您還不<b class='flag-5'>知道</b>的 TINA-TI 某些資源! (IV)

    一些也許您還不知道的 TINA -TI 某些資源! (III)

    一些也許您還不知道的 TINA -TI 某些資源! (III)
    發(fā)表于 11-07 08:07 ?3次下載
    <b class='flag-5'>一些</b>也許您還不<b class='flag-5'>知道</b>的 TINA -TI 某些資源! (III)

    一些也許您還不知道的 TINA-TI 某些資源! (II)

    一些也許您還不知道的 TINA-TI 某些資源! (II)
    發(fā)表于 11-07 08:07 ?2次下載
    <b class='flag-5'>一些</b>也許您還不<b class='flag-5'>知道</b>的 TINA-TI 某些資源! (II)

    一些也許您還不知道的 TINA-TI 的那些資源!

    一些也許您還不知道的 TINA-TI 的那些資源!
    發(fā)表于 11-07 08:07 ?3次下載
    <b class='flag-5'>一些</b>也許您還不<b class='flag-5'>知道</b>的 TINA-TI 的那些資源!