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:配列の生成コストが若干気になるが誤差だよ誤差。