ホームページ >データベース >Redis >Redis キャッシュスペースを最適化する方法

Redis キャッシュスペースを最適化する方法

PHPz
PHPz転載
2023-05-27 23:44:061215ブラウズ

シーン設定

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","投資家":"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}

上記には多くのものが含まれていることがわかりました。属性名を含む無駄なデータは保存する必要はありません。

改善 1-属性名の削除

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","処理中"]

null を格納する必要がないことがわかりました。時刻形式は文字列にシリアル化されます。不当なシリアル化の結果はデー​​タの拡張につながるため、より適切なシリアル化ツールを選択する必要があります。

改善 2 - より優れたシリアル化ツールを使用する

//我们仍然选取JSON格式,但使用了第三方序列化工具
System.out.println(new ObjectMapper(new MessagePackFactory()).writeValueAsBytes(pojo.toValueArray()).length);

より優れたシリアル化ツールを選択して、フィールド圧縮と適切なデータ形式を実現します。print** length=92、スペースが削減されます。前のステップと比較して 40% 増加しました。

これはバイナリ データです。Redis はバイナリで操作する必要があります。バイナリを文字列に変換した後、次のように出力します:

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

このアイデアに従ってさらに掘り下げて、データ型を手動で選択することで、より極端な最適化効果を達成できることがわかりました。より小さいデータ型を使用することを選択すると、さらに改善が得られます。

改善 3-データ型の最適化

上記の使用例では、testStatus、preCheckStatus、investor の 3 つのフィールドは実際には列挙文字列型です。文字列の代わりにデータ型 (byte や int など) を使用すると、スペースをさらに節約できます。 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;
}

手動調整後、文字列型の代わりに小さなデータ型が使用され、印刷されますlength=69

改善点 4-ZIP 圧縮の検討

上記の点に加えて、より小さなボリュームを取得するために ZIP 圧縮の使用を検討することもできます。コンテンツが大きい場合や繰り返しが多い場合、ZIP 圧縮の効果は明ら​​かです。ストレージがコンテンツの場合、コンテンツは配列ですおそらく ZIP 圧縮での使用に適しています。

30 バイト未満のファイルの場合、ZIP 圧縮によってファイル サイズが増加する可能性がありますが、必ずしもファイル サイズが減少するとは限りません。繰り返しの少ないコンテンツの場合、大幅な改善は得られません。そして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 個のフィールドが含まれます。フィールド キーの長さは 6 バイトで、フィールドは個別に管理されます。

通常の状況では、ハッシュ構造を考えますが、ハッシュ構造にはキー情報が格納されるため、追加のリソースが占有されます。フィールドのキーは不要なデータです。上記の考え方によれば、代わりにリストを使用できます。ハッシュ構造の。

Redis 公式ツールのテストによると、リスト構造の使用には 144G のスペースが必要ですが、ハッシュ構造の使用には 245G のスペースが必要です** (属性の 50% 以上が空の場合は、これはまだ適用可能です)* *

Redis キャッシュスペースを最適化する方法

上記の例では、数行の簡単なコードで非常に簡単な措置をいくつか講じました。これにより、スペースをさらに削減できます。データ量が比較的大きい場合、大規模で高いパフォーマンス要件が必要なシナリオでは、これを強くお勧めします。 :

• オブジェクトの代わりに配列を使用します (多数のフィールドが空の場合は、シリアル化ツールを使用して null を圧縮する必要があります)

• より適切なシリアル化ツールを使用します

•より小さいデータ型を使用します

• ZIP 圧縮の使用を検討してください

• ハッシュ構造の代わりにリストを使用します (多数のフィールドが空の場合は、テストと比較が必要です)

以上がRedis キャッシュスペースを最適化する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。