您好,登錄后才能下訂單哦!
這篇文章主要介紹“Redis緩存空間怎么優化”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“Redis緩存空間怎么優化”文章能幫助大家解決問題。
1、我們需要將POJO存儲到緩存中,該類定義如下
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){ //具體的數據類型會丟失,需要做處理 } }
2、用下面的實例作為測試數據
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");
System.out.println(JSON.toJSONString(pojo).length());
使用JSON直接序列化、打印 length=284**,**這種方式是最簡單的方式,也是最常用的方式,具體數據如下:
{"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}
我們發現,以上包含了大量無用的數據,其中屬性名是沒有必要存儲的。
System.out.println(JSON.toJSONString(pojo.toValueArray()).length());
通過選擇數組結構代替對象結構,去掉了屬性名,打印 length=144,將數據大小降低了50%,具體數據如下:
["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"]
我們發現,null是沒有必要存儲的,時間的格式被序列化為字符串,不合理的序列化結果,導致了數據的膨脹,所以我們應該選用更好的序列化工具。
//我們仍然選取JSON格式,但使用了第三方序列化工具 System.out.println(new ObjectMapper(new MessagePackFactory()).writeValueAsBytes(pojo.toValueArray()).length);
選取更好的序列化工具,實現字段的壓縮和合理的數據格式,打印 **length=92,**空間比上一步又降低了40%。
這是一份二進制數據,需要以二進制操作Redis,將二進制轉為字符串后,打印如下:
??SUCCESS?ABCDEFGHIJ?ABCD??j?6???XX??j?6?????`bM????@i??Q?Y?2023-02-02?PROCESSING
順著這個思路再深挖,我們發現,可以通過手動選擇數據類型,實現更極致的優化效果,選擇使用更小的數據類型,會獲得進一步的提升。
在以上用例中,testStatus、preCheckStatus、investor這3個字段,實際上是枚舉字符串類型,如果能夠使用更簡單數據類型(比如byte或者int等)替代string,還可以進一步節省空間。其中checkTime可以用Long類型替代字符串,會被序列化工具輸出更少的字節。
public Object[] toValueArray(){ Object[] array = {toInt(testStatus), userPin, toInt(investor), testQueryTime, createTime, bizInfo, otherTime, userAmount, userRate, applyAmount, type, toLong(checkTime), toInt(preTestStatus)}; return array; }
在手動調整后,使用了更小的數據類型替代了String類型,打印 length=69
除了以上的幾點之外,還可以考慮使用ZIP壓縮方式獲取更小的體積,在內容較大或重復性較多的情況下,ZIP壓縮的效果明顯,如果存儲的內容是TestPOJO的數組,可能適合使用ZIP壓縮。
但ZIP壓縮并不一定會減少體積,在小于30個字節的情況下,也許還會增加體積。在重復性內容較少的情況下,無法獲得明顯提升。并且存在CPU開銷。
在經過以上優化之后,ZIP壓縮不再是必選項,需要根據實際數據做測試才能分辨到ZIP的壓縮效果。
上面的幾個改進步驟體現了優化的思路,但是反序列化的過程會導致類型的丟失,處理起來比較繁瑣,所以我們還需要考慮反序列化的問題。
在緩存對象被預定義的情況下,我們完全可以手動處理每個字段,所以在實戰中,推薦使用手動序列化達到上述目的,實現精細化的控制,達到最好的壓縮效果和最小的性能開銷。
可以參考以下msgpack的實現代碼,以下為測試代碼,請自行封裝更好的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()); } }
假設,我們為2億用戶存儲數據,每個用戶包含40個字段,字段key的長度是6個字節,字段是分別管理的。
正常情況下,我們會想到hash結構,而hash結構存儲了key的信息,會占用額外資源,字段key屬于不必要數據,按照上述思路,可以使用list替代hash結構。
通過Redis官方工具測試,使用list結構需要144G的空間,而使用hash結構需要245G的空間**(當50%以上的屬性為空時,需要進行測試,是否仍然適用)**
在以上案例中,我們采取了幾個非常簡單的措施,僅僅有幾行簡單的代碼,可降低空間70%以上,在數據量較大以及性能要求較高的場景中,是非常值得推薦的。:
• 使用數組替代對象(如果大量字段為空,需配合序列化工具對null進行壓縮)
• 使用更好的序列化工具
• 使用更小的數據類型
• 考慮使用ZIP壓縮
• 使用list替代hash結構(如果大量字段為空,需要進行測試對比)
關于“Redis緩存空間怎么優化”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。