最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redis緩存空間優(yōu)化實(shí)踐詳解

 更新時(shí)間:2023年04月20日 11:09:01   作者:京東云開(kāi)發(fā)者  
緩存Redis,是我們最常用的服務(wù),其適用場(chǎng)景廣泛,被大量應(yīng)用到各業(yè)務(wù)場(chǎng)景中。也正因如此,緩存成為了重要的硬件成本來(lái)源,我們有必要從空間上做一些優(yōu)化,降低成本的同時(shí)也會(huì)提高性能,本文通過(guò)代碼示例介紹了redis如何優(yōu)化緩存空間,需要的朋友可以參考一下

導(dǎo)讀

緩存Redis,是我們最常用的服務(wù),其適用場(chǎng)景廣泛,被大量應(yīng)用到各業(yè)務(wù)場(chǎng)景中。也正因如此,緩存成為了重要的硬件成本來(lái)源,我們有必要從空間上做一些優(yōu)化,降低成本的同時(shí)也會(huì)提高性能。

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

場(chǎng)景設(shè)定

1、我們需要將POJO存儲(chǔ)到緩存中,該類(lèi)定義如下

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){         
        //具體的數(shù)據(jù)類(lèi)型會(huì)丟失,需要做處理
    }
}

2、用下面的實(shí)例作為測(cè)試數(shù)據(jù)

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");

常規(guī)做法

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

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

{"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}

我們發(fā)現(xiàn),以上包含了大量無(wú)用的數(shù)據(jù),其中屬性名是沒(méi)有必要存儲(chǔ)的。

改進(jìn)1-去掉屬性名

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

通過(guò)選擇數(shù)組結(jié)構(gòu)代替對(duì)象結(jié)構(gòu),去掉了屬性名,打印 length=144,將數(shù)據(jù)大小降低了50%,具體數(shù)據(jù)如下:

["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"]

我們發(fā)現(xiàn),null是沒(méi)有必要存儲(chǔ)的,時(shí)間的格式被序列化為字符串,不合理的序列化結(jié)果,導(dǎo)致了數(shù)據(jù)的膨脹,所以我們應(yīng)該選用更好的序列化工具。

改進(jìn)2-使用更好的序列化工具

//我們?nèi)匀贿x取JSON格式,但使用了第三方序列化工具
System.out.println(new ObjectMapper(new MessagePackFactory()).writeValueAsBytes(pojo.toValueArray()).length);

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

這是一份二進(jìn)制數(shù)據(jù),需要以二進(jìn)制操作Redis,將二進(jìn)制轉(zhuǎn)為字符串后,打印如下:

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

順著這個(gè)思路再深挖,我們發(fā)現(xiàn),可以通過(guò)手動(dòng)選擇數(shù)據(jù)類(lèi)型,實(shí)現(xiàn)更極致的優(yōu)化效果,選擇使用更小的數(shù)據(jù)類(lèi)型,會(huì)獲得進(jìn)一步的提升。

改進(jìn)3-優(yōu)化數(shù)據(jù)類(lèi)型

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

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

在手動(dòng)調(diào)整后,使用了更小的數(shù)據(jù)類(lèi)型替代了String類(lèi)型,打印 length=69

改進(jìn)4-考慮ZIP壓縮

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

但ZIP壓縮并不一定會(huì)減少體積,在小于30個(gè)字節(jié)的情況下,也許還會(huì)增加體積。在重復(fù)性內(nèi)容較少的情況下,無(wú)法獲得明顯提升。并且存在CPU開(kāi)銷(xiāo)。

在經(jīng)過(guò)以上優(yōu)化之后,ZIP壓縮不再是必選項(xiàng),需要根據(jù)實(shí)際數(shù)據(jù)做測(cè)試才能分辨到ZIP的壓縮效果。

最終落地

上面的幾個(gè)改進(jìn)步驟體現(xiàn)了優(yōu)化的思路,但是反序列化的過(guò)程會(huì)導(dǎo)致類(lèi)型的丟失,處理起來(lái)比較繁瑣,所以我們還需要考慮反序列化的問(wèn)題。

在緩存對(duì)象被預(yù)定義的情況下,我們完全可以手動(dòng)處理每個(gè)字段,所以在實(shí)戰(zhàn)中,推薦使用手動(dòng)序列化達(dá)到上述目的,實(shí)現(xiàn)精細(xì)化的控制,達(dá)到最好的壓縮效果和最小的性能開(kāi)銷(xiāo)。

可以參考以下msgpack的實(shí)現(xiàn)代碼,以下為測(cè)試代碼,請(qǐng)自行封裝更好的Packer和UnPacker等工具:

<dependency>    
    <groupId>org.msgpack</groupId>    
    <artifactId>msgpack-core</artifactId>    
    <version>0.9.3</version>
</dependency>
    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());
        }
    }

場(chǎng)景延伸

假設(shè),我們?yōu)?億用戶存儲(chǔ)數(shù)據(jù),每個(gè)用戶包含40個(gè)字段,字段key的長(zhǎng)度是6個(gè)字節(jié),字段是分別管理的。

正常情況下,我們會(huì)想到hash結(jié)構(gòu),而hash結(jié)構(gòu)存儲(chǔ)了key的信息,會(huì)占用額外資源,字段key屬于不必要數(shù)據(jù),按照上述思路,可以使用list替代hash結(jié)構(gòu)。

通過(guò)Redis官方工具測(cè)試,使用list結(jié)構(gòu)需要144G的空間,而使用hash結(jié)構(gòu)需要245G的空間**(當(dāng)50%以上的屬性為空時(shí),需要進(jìn)行測(cè)試,是否仍然適用)**

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

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

• 使用更好的序列化工具

• 使用更小的數(shù)據(jù)類(lèi)型

• 考慮使用ZIP壓縮

• 使用list替代hash結(jié)構(gòu)(如果大量字段為空,需要進(jìn)行測(cè)試對(duì)比)

以上就是Redis緩存空間優(yōu)化實(shí)踐的詳細(xì)內(nèi)容,更多關(guān)于Redis緩存空間優(yōu)化的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • redis中如何做到內(nèi)存優(yōu)化

    redis中如何做到內(nèi)存優(yōu)化

    為了提高數(shù)據(jù)處理效率和降低存儲(chǔ)成本,優(yōu)化數(shù)據(jù)結(jié)構(gòu)和采用高效的存儲(chǔ)策略至關(guān)重要,使用最小存儲(chǔ)形式、整數(shù)編碼、Redis的HyperLogLog等方法可以有效減少內(nèi)存占用,Redis6引入的對(duì)象壓縮、設(shè)置合理的過(guò)期時(shí)間、數(shù)據(jù)分片
    2024-09-09
  • Redis 內(nèi)存淘汰策略深度解析(最新推薦)

    Redis 內(nèi)存淘汰策略深度解析(最新推薦)

    本文詳細(xì)探討了Redis的內(nèi)存淘汰策略、實(shí)現(xiàn)原理、適用場(chǎng)景及最佳實(shí)踐,介紹了八種內(nèi)存淘汰策略,包括noeviction、LRU、LFU、TTL、Random等,并分析了它們的應(yīng)用場(chǎng)景,同時(shí),文章還介紹了Redis內(nèi)存淘汰的底層實(shí)現(xiàn),包括LRU/LFU的近似算法和淘汰流程,感興趣的朋友一起看看吧
    2025-03-03
  • Linux環(huán)境下升級(jí)redis的詳細(xì)步驟記錄

    Linux環(huán)境下升級(jí)redis的詳細(xì)步驟記錄

    這篇文章主要給大家介紹了關(guān)于Linux環(huán)境下升級(jí)redis的詳細(xì)步驟,描述了如何從舊版本升級(jí)到新版本Redis,包括備份舊數(shù)據(jù)、下載和安裝新版本、復(fù)制配置文件和數(shù)據(jù)、停止舊版本并啟動(dòng)新版本的過(guò)程,需要的朋友可以參考下
    2024-12-12
  • 詳解Redis高效恢復(fù)策略內(nèi)存快照與AOF

    詳解Redis高效恢復(fù)策略內(nèi)存快照與AOF

    這篇文章主要為大家介紹了Redis高效恢復(fù)策略內(nèi)存快照與AOF及對(duì)比詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Caffeine實(shí)現(xiàn)類(lèi)似redis的動(dòng)態(tài)過(guò)期時(shí)間設(shè)置示例

    Caffeine實(shí)現(xiàn)類(lèi)似redis的動(dòng)態(tài)過(guò)期時(shí)間設(shè)置示例

    這篇文章主要為大家介紹了Caffeine實(shí)現(xiàn)類(lèi)似redis的動(dòng)態(tài)過(guò)期時(shí)間示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-08-08
  • 如何提高Redis服務(wù)器的最大打開(kāi)文件數(shù)限制

    如何提高Redis服務(wù)器的最大打開(kāi)文件數(shù)限制

    文章討論了如何提高Redis服務(wù)器的最大打開(kāi)文件數(shù)限制,以支持高并發(fā)服務(wù),本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧
    2025-01-01
  • Redis 中spark參數(shù)executor-cores引起的異常解決辦法

    Redis 中spark參數(shù)executor-cores引起的異常解決辦法

    這篇文章主要介紹了Redis 中spark參數(shù)executor-cores引起的異常解決辦法的相關(guān)資料,需要的朋友可以參考下
    2017-03-03
  • 關(guān)于Redis數(shù)據(jù)庫(kù)入門(mén)詳細(xì)介紹

    關(guān)于Redis數(shù)據(jù)庫(kù)入門(mén)詳細(xì)介紹

    大家好,本篇文章主要講的是關(guān)于Redis數(shù)據(jù)庫(kù)入門(mén)詳細(xì)介紹,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • 基于redis.properties文件的配置及說(shuō)明介紹

    基于redis.properties文件的配置及說(shuō)明介紹

    今天小編就為大家分享一篇基于redis.properties文件的配置及說(shuō)明介紹,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • redis?bitmap數(shù)據(jù)結(jié)構(gòu)之java對(duì)等操作詳解

    redis?bitmap數(shù)據(jù)結(jié)構(gòu)之java對(duì)等操作詳解

    bitmap是以其高性能出名。其基本原理是一位存儲(chǔ)一個(gè)標(biāo)識(shí),其他衍生知道咱就不說(shuō)了,而redis就是以這種原生格式存儲(chǔ)的,這篇文章主要介紹了redis?bitmap數(shù)據(jù)結(jié)構(gòu)之java對(duì)等操作,需要的朋友可以參考下
    2022-10-10

最新評(píng)論

栾城县| 灵山县| 封丘县| 安化县| 英吉沙县| 台北市| 汾阳市| 旺苍县| 肇东市| 梅河口市| 奇台县| 高安市| 宜州市| 浦东新区| 科尔| 丰城市| 麻江县| 额济纳旗| 平阴县| 南川市| 略阳县| 沅陵县| 普定县| 越西县| 资中县| 昌邑市| 扶余县| 汉阴县| 迭部县| 图木舒克市| 陈巴尔虎旗| 尉犁县| 砚山县| 芜湖县| 伊宁县| 成安县| 吴堡县| 荔浦县| 梁山县| 临江市| 远安县|