あいつの日誌β

働きながら旅しています。

MacOSX で redis3.0.0 の cluster を試してみた

install redis3.0.0 to MacOSX

brew ではまだ 3.0.0 の用意がない模様

cd ~/Downloads
curl -O http://download.redis.io/releases/redis-3.0.0.tar.gz
tar zxvf redis-3.0.0.tar.gz 
cd redis-3.0.0
make test
make
make install

もし brew で管理されている redis 2.8.19 に戻したい場合は rm -fr /usr/local/bin/redis-* してから brew uninstall redis からの brew install redis すればいける気がします(あんまりよくわかっていない)

Cluster

こちらを参考にして準備します

http://toufuegg.hatenablog.com/entry/2015/04/04/170309

% mkdir ~/cluster-test && cd $_
% mkdir 700{0,1,2,3,4,5}
% cat <<EOS > redis.conf
port 7000
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
appendonly yes
EOS
% for i in {0..5}; do cp -av redis.conf "700$i/"; sed -i -e "s/port 7000/port 700$i/g" "700$i/redis.conf"; done

作成したディクレトリに移動してから redis-server redis.conf を実行する。 たぶんバックグラウンドで動かすよりもフォアグランドで動かしたほうがログ見やすい気がします。

% cd 7000 && redis-server redis.conf
% cd 7001 && redis-server redis.conf
% cd 7002 && redis-server redis.conf
% cd 7003 && redis-server redis.conf
% cd 7004 && redis-server redis.conf
% cd 7005 && redis-server redis.conf

redis-3.0.0 に同梱されている ruby スクリプトを使用します。 gem install redis しないと動かないので rbenv などで各自 ruby のセットアップをお願いします。

% cd ~/Downloads/redis-3.0.0/src  
% ./redis-trib.rb create --replicas 0 \
  127.0.0.1:7000 \
  127.0.0.1:7001 \
  127.0.0.1:7002 \
  127.0.0.1:7003

>>> Creating cluster
Connecting to node 127.0.0.1:7000: OK
Connecting to node 127.0.0.1:7001: OK
Connecting to node 127.0.0.1:7002: OK
Connecting to node 127.0.0.1:7003: OK
>>> Performing hash slots allocation on 4 nodes...
Using 4 masters:
127.0.0.1:7000
127.0.0.1:7001
127.0.0.1:7002
127.0.0.1:7003
M: 05197142d6b02fdd3f7c68214082f859892facf0 127.0.0.1:7000
   slots:0-4095 (4096 slots) master
M: c4f6d0d61b3542749d6a39afb26979bd8cf88744 127.0.0.1:7001
   slots:4096-8191 (4096 slots) master
M: 23de6cb0a1e609bac71e4f4daa32ea4923cd2d22 127.0.0.1:7002
   slots:8192-12287 (4096 slots) master
M: e16004ece64c55bd43562f11c4a6f1a1ecb866fb 127.0.0.1:7003
   slots:12288-16383 (4096 slots) master
Can I set the above configuration? (type 'yes' to accept): yes とタイプしてから Enter

以下で確認します。

% redis-cli -p 7000 cluster nodes
23de6cb0a1e609bac71e4f4daa32ea4923cd2d22 127.0.0.1:7002 master - 0 1428304456836 3 connected 8192-12287
e16004ece64c55bd43562f11c4a6f1a1ecb866fb 127.0.0.1:7003 master - 0 1428304455802 4 connected 12288-16383
c4f6d0d61b3542749d6a39afb26979bd8cf88744 127.0.0.1:7001 master - 0 1428304455285 2 connected 4096-8191
05197142d6b02fdd3f7c68214082f859892facf0 127.0.0.1:7000 myself,master - 0 0 1 connected 0-4095

この後 re-sharding などの動作確認を試したい方はこちらの記事がいい感じです。是非実際に動作検証してみてください。 http://toufuegg.hatenablog.com/entry/2015/04/04/170309

本題

redis-server がクラスタリングしている場合、クライアントはどうやって値を取得しているのか、というのがこの記事の趣旨です。

値を SET すると 7000 -> 7002 へダイレクトされる

% redis-cli -c -p 7000
127.0.0.1:7000> set foo bar
-> Redirected to slot [12182] located at 127.0.0.1:7002
OK
127.0.0.1:7002> get foo
"bar"
127.0.0.1:7002> Quit

再度 7000 port へ接続して GET してみる

% redis-cli -c -p 7000 
127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
"bar"
127.0.0.1:7002> Quit

接続先が切り替わっている事が確認できます。

低能なクライアントと賢いクライアント

redis-server がクラスタリングしている場合、クライアント側では2つの挙動が存在します。 よく見ると先ほどの redis-cli コマンドは -c をオプションにつけています。

% redis-cli -h
(中略)
-c Enable cluster mode (follow -ASK and -MOVED redirections).

Enable cluster mode は ASK redirections と Moved redirections という挙動があるようです。

MOVED redirections は先ほどの挙動です。7000 port の redis-server プロセスに SET, GET を試みているのですがその都度 7000 port の redis-server に「いや、その Key はこっちじゃなくてあっちに行かないとだめよ、だめ(ry」と言われてしまっています。

つまり2回通信が発生しています。これが低能なクライアントの挙動です(実はあんまりわかっていない)

もうひとつ ASK redirection という方式があり、こちらは賢い挙動をします。

賢いクライアントは初回のリクエスト時に各 redis-server へのコネクション、ハッシュスロットとノードの対応表を保存しておき、最初から目当てのノードに対してリクエストします(たぶん) もし対応表に変更があれば、また初回リクエスト時と同じように各種情報をサーバーから返してもらい、次回から目当てのノードに対して直接リクエストします。

http://redis.shibu.jp/admin/cluster/#id3

パイプライニングやマルチコマンドで複数のノードをまたぐ場合の挙動

とりあえず mget はだめだった

% redis-cli -c -p 7000
127.0.0.1:7000> get foo
-> Redirected to slot [12182] located at 127.0.0.1:7002
"bar"
127.0.0.1:7002> set key2 val2
-> Redirected to slot [4998] located at 127.0.0.1:7001
OK
127.0.0.1:7001> set key1 val1
-> Redirected to slot [9189] located at 127.0.0.1:7002
OK
127.0.0.1:7002> mget key1 key2
(error) CROSSSLOT Keys in request don't hash to the same slot

パイプライニング は期待した結果にならない

% (printf "get key1\r\nget key2\r\nget key1\r\n"; sleep 1) | nc localhost 7000
-MOVED 9189 127.0.0.1:7002
-MOVED 4998 127.0.0.1:7001
-MOVED 9189 127.0.0.1:7002

まとめ

既存のサービスが mget を多用している場合はクラスタリングはちょっと厳しいかも。