kagamihogeの日記

kagamihogeの日記です。

RedisのKEYSを同時に何個か実行する

RedisのkeysがO(N)を実際に見る で見たようにKEYSは登録キー件数によってはかなり遅くなる。次に、特に重いKEYS *を同時に複数実行すると、タイムアウトするらしいので、そこを実際にやってみる。

ソースコード

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

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

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

後述するがタイムアウトを防止するため、以下のプロパティをapplication.ymlに設定する。

spring:
  redis:
    host: localhost
    timeout: 10000000

下記のような感じで、5スレッドでKEYS *を実行する。また、開始前にあらかじめkey0~10,000,000で1千万件エントリ作っておく。

        ExecutorService es = Executors.newFixedThreadPool(5);
        
        for (int i=0; i < 5; i++) {
            es.submit(() -> {
                System.out.println("start");
                long start = System.currentTimeMillis();
                Set<String> keys = redis.keys("*");
                
                System.out.println("##" + (System.currentTimeMillis() - start) + " " + keys.size());
            });
        }

        es.shutdown();

実行結果

30554
42794
57868
73334
88105

後のスレッドになればなるほど実行時間がかかっているのが分かる

そこでjvisualvmでスレッドを見る。すると、キレイに前のスレッドが終わってから次のスレッド、になっている。スレッド開始はほぼ同時なので、後のスレッドになるほど実行時間がかかるのと合致する。

f:id:kagamihoge:20190827213304j:plain

各種文献にあるとおり、Redisはシングルスレッドでリクエストを処理する。このため、同時にKEYS *を走らせても一つずつしか処理しない。つまり、redisへのリクエストはマルチで出せるが、2スレッド目以降は先行スレッドが終わらない限り待たされる。

また、spring-data-redisのデフォルトタイムアウトは60秒*1。なのでspring.redis.host.timeout: 10000000が無いと、上記コードは3スレッド目以降はタイムアウトする。よって、エントリ数次第ではKEYS *を下手に乱発するとタイムアウト頻発になる可能性が高い。

というわけで、マニュアルにある通りKEYS *は慎重に使うもの、という事が分かる。

KEYS(pattern)

言い換えると、このコマンドはデバッグやデータベースのスキーマの変更を行うなどの特別な操作を除いて使うべきではありません。通常のコードでは使わないでください。

http://redis.shibu.jp/commandreference/alldata.html#command-KEYS より抜粋

参考文献

*1:ドキュメント探せなかったが実際にやってみると60秒でタイムアウトした