kagamihogeの日記

kagamihogeの日記です。

RedisのLISTに1件ずつとまとめてPUSHするときの速度差

Redisをはじめて触るので色々と試している。とりあえず基本的なパフォーマンスを見るということで、LISTに1件ずつPUSHする場合と、ある程度まとめてPUSHする場合の速度差を見る。それによってRedisを学ぶのが目的である。

ソースコード

pom.xml

 <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.5.RELEASE</version>
        <relativePath />
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>10</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
    </dependencies>

Java

100万件連番の数字をLISTにL/RPUSHしていく。速度計測はお手軽にSystem.currentTimeMillis()の差で行う。また、一度実行するたびにキャッシュはキーを指定して削除している。

1件ずつPUSHの場合。

        long start = System.currentTimeMillis();
        
        BoundListOperations<String, String> ops = redis.boundListOps("list001");
        for (int i=0; i<1_000_000; i++) {
//            ops.leftPush(String.valueOf(i));
            ops.rightPush(String.valueOf(i));
        }
        
        System.out.println(System.currentTimeMillis() - start);

10万件ずつ10回PUSHの場合。*1

        long start = System.currentTimeMillis();
        
        BoundListOperations<String, String> ops = redis.boundListOps("list001");
        for (int i=0; i<10; i++) {
            String[] array = IntStream.range(0, 100_000).mapToObj(n -> String.valueOf(n)).toArray(String[]::new);
//            ops.leftPushAll(array);
            ops.rightPushAll(array);
        }
        
        System.out.println(System.currentTimeMillis() - start);

計測結果

1 2 3
leftPush 123020 122401 122132
rightPush 122080 121225 123555
leftPushAll 856 772 1080
rightPushAll 821 782 771

以下はn回目のPUSHに要した時間。最初の5回だけで以降は省略。

1 2 3 4 5
296 1 1 0 2
all 401 59 44 30 31

感想とか

やはり通信が発生する以上、1件ずつ追加するよりまとめて追加する方が圧倒的に早い。このあたりはRDBMSと同様、オーバーヘッドの削減が実行時間に関係する。

単純にRPUSHもしくはLPUSHする場合は実行時間に差は見られない。これはマニュアルに「Redisリストは双方向リスト~」とあるので、それが確認できている。

PUSHそのものの実行時間について。まず、最初の1回目だけはリストの初期化処理が何らかあるらしく、ほんのちょっと遅い。

次に2回目以降について。1件より10万件の方が遅いが、極めて僅かな差でしかない。PUSH一回の時間がほぼ同じとなると、まとめてPUSHすればするほど早くなると思われる。どの位のデータ量が分岐点になるかは今回のコードからは見えてこないが、あまりデカい配列作るとメモリの方が気になりそう。

ところで、一気に大量に追加するコストがそもそも相当に安いのだが、1件の場合はもっと早くてSystem.currentTimeMillis()で差が取れないくらいである(ただこの計測はJavaとRedisを同一マシンでやっているので、リモートの場合はまた別かもしれない)。

PUSHのコストは安いとなると、そんなに頻繁なPUSHが発生せず実行時間がさほど気にならないのであれば、1件ずつPUSHでもまぁよくね? となるかもしれない。逆に、極めて頻繁にPUSHが発生するのなら、なるべくまとめてPUSHすれば、実行時間の改善に寄与する可能性が高い。

といっても、高速なPUSHを活かすのがRedisの用途だから、バッチ的なバルク処理はあんま無かったりするんですかね?

*1:配列の生成コストが若干気になるが誤差だよ誤差。