RedisをマルチAZに配置したときの問題
az-1に配置したRedisに対して、az-2に配置したアプリケーションからアクセスすると、az間のネットワークレイテンシの問題で遅くなるという話。
Redis Pipeliningとは
Spring Data Redis - 10.13. Pipelining
Redis provides support for pipelining, which involves sending multiple commands to the server without waiting for the replies and then reading the replies in a single step.
通常は1コマンドごとにサーバに送られるので、大量のデータをRedisとやりとりすると、サーバとの通信も大量に発生することになります。100件のsetで100回通信が発生するのはもちろん、100件のgetでも100回通信が発生します。(setやhashも同様)
Redis RepositoriesのfindByXXとかやると、裏でkeys + レコード数分のhgetallが走るので、通信量が膨大になります。
Pipeliningは、上記の通り複数のコマンドを1度の通信で送るので、ネットワークレイテンシの問題が解消するのでは?という仮説のもと検証をしました。
検証内容
配置パターン
ローカル、AWS(同一az)、AWS(azまたがり)の3パターンを検証。
ローカル
ローカルPCのJavaアプリから、ローカルPCのRedisコンテナ on Docker Desktopへアクセスする構成。
ThinkPad X390
redis 6.0.9 (docker run -p 6379:6379 redis)
openjdk version "11.0.2" 2019-01-15
Spring Data Redis 2.4.2
AWS(同一VPC、同一az)
同一VPC上、同一az内に、Redisサーバ、Javaクライアントのサーバをそれぞれ起動して、同一az内でアクセスする構成。
・Redisサーバ
東京リージョン、ap-northeast-1c
Amazon Linux2
m5.xlarge
Redis 6.0.9
・Javaクライアント
東京リージョン、ap-northeast-1c
Amazon Linux2
t2.micro
openjdk version "11.0.9.1" 2020-11-04 LTS(Coretto)
Spring Data Redis 2.4.2
AWS(同一VPC、別az)
同一VPC上、異なるaz内に、Redisサーバ、Javaクライアントのサーバをそれぞれ起動して、azまたがりでアクセスする構成。
・Redisサーバ
東京リージョン、ap-northeast-1c
Amazon Linux2
m5.xlarge
Redis 6.0.9
・Javaクライアント
東京リージョン、ap-northeast-1d
Amazon Linux2
t2.micro
openjdk version "11.0.9.1" 2020-11-04 LTS(Coretto)
Spring Data Redis 2.4.2
動作検証アプリ
レコード数
1, 10, 100, 1000の4パターンを検証。
Redisへのアクセスパターン
setをレコード数分x3回、getをレコード数分x3回やって、set/getそれぞれの平均処理時間[ms]を計測。
setは、"key[n]", "value[n]"
getは、"keys *"した後、それぞれのkeyをgetする
Redisへのアクセス方法
下記3パターンを検証。
* 1つずつコマンドを発行する方法
* Pipeliningを使ってコマンドを発行する方法
* Parallel Streamを使ってコマンドを発行する方法
検証結果
縦軸は処理時間[ms]です。
ローカル
AWS(同一az)
AWS(azまたがり)
考察
- Pipeliningは少量のレコードを扱う場合のオーバーヘッドが結構ある(1件の処理でnormalやParallel Steramに比べて倍以上時間がかかっている)。
- azまたがりのケースでは、Pipeliningは有効であるが、100件まではParallel Streamとほとんど変わらなかった。1000件でようやく差が出た。
- Parallel Streamかなり優秀。とはいえ、裏でスレッドを生成して実行する仕組みなので、CPU負荷がかかることに注意する。
結論
Redis Pipeliningはネットワークレイテンシの壁を超えるけど、オーバーヘッドもあるので、数百~千件単位のバルク処理にのみ使うのが良い。
Javaの場合、CPU負荷との兼ね合いにもなるが、ほとんどのケースでは、Parallel Steramを使っておけば解決するのではないかと思う。