前回、
なお、
データの保存・取得とリクエストの転送
memcachedでは、
一方Kaiでは、
では、
次のようなPerl Scriptを用意し、
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More tests => 3;
use Cache::Memcached;
my $set_only_mem = Cache::Memcached->new({
servers => [map {'127.0.0.1:1121' . $_} (1..3)],
});
$set_only_mem->set(foo => 'bar', 0,); # Consistent Hashing により一つのノードを選択し、データを保存する
for (1..3) {
my $get_only_mem = Cache::Memcached->new({
servers => ['127.0.0.1:1121' . $_],
});
is $get_only_mem->get('foo'), 'bar'; # 全てのノードから同じ値が取得できる
}
memcachedと異なり、
では、
$ perl /path/to/cluster_test.pl 1..3 ok 1 ok 2 ok 3
このような実行結果となれば、
ノードの追加・除去による自動データ再配置
memcachedでは、
一方Kaiでは、
では、
まず始めに、
$ /path/to/make_kai_config.sh 4
続けて、
$ /path/to/start_kai.sh kai4
次に、
$ /path/to/remsh.sh kai1 Erlang R13B (erts-5.7.1) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.7.1 (abort with ^G) (kai1@centos)1> kai_rpc:check_node({{127,0,0,1}, 11011}, {{127,0,0,1}, 11014}). % (a) ok (kai1@centos)2> kai_rpc:check_node({{127,0,0,1}, 11014}, {{127,0,0,1}, 11011}). % (b) ok (kai1@centos)3> <- 作業が終わったので、local shell に切り替える User switch command --> s --> c Eshell V5.7.1 (abort with ^G) (kai_controller@centos)1> q(). ok
前回は、
まず (a) で、
node_
各ノードは定期的に、
最後に (b) で、

それでは、
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More tests => 1;
use Cache::Memcached;
my $mem = Cache::Memcached->new({
servers => ['127.0.0.1:11214'],
});
is $mem->get('foo'), 'bar';
用意したPerl Scriptを実行し、
$ perl /path/to/kai_test_cluster_get4.pl 1..1 ok 1
このような実行結果となれば、
では、
まず始めに、
$ /path/to/make_kai_config.sh 5 6 $ /path/to/start_kai.sh kai5 kai6
次に、
$ /path/to/remsh.sh kai1 Erlang R13B (erts-5.7.1) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.7.1 (abort with ^G) (kai1@centos)1> kai_rpc:check_node({{127,0,0,1}, 11014}, {{127,0,0,1}, 11015}). % (c) ok (kai1@centos)2> kai_rpc:check_node({{127,0,0,1}, 11015}, {{127,0,0,1}, 11016}). % (d) ok (kai1@centos)3> kai_rpc:check_node({{127,0,0,1}, 11016}, {{127,0,0,1}, 11014}). % (e) ok (kai1@centos)4> User switch command --> q
基本的には、
(c) で、
(d) で、
(e) で、

これで、
クラスタが3個のノードで構成されていた頃から、
次のようなPerl Scriptを用意し、
#!/usr/bin/env perl
use strict;
use warnings;
use Test::More tests => 3;
use Cache::Memcached;
for (4..6) {
my $mem = Cache::Memcached->new({
servers => ['127.0.0.1:1121' . $_],
});
is $mem->get('foo'), 'bar';
}
用意したPerl Scriptを実行し、
$ perl /path/to/kai_test_cluster_get456.pl 1..3 ok 1 ok 2 ok 3
このような実行結果となれば、
では、
ここでは、
$ /path/to/remsh.sh kai1 Erlang R13B (erts-5.7.1) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.7.1 (abort with ^G) (kai1@centos)1> q(). ok (kai1@centos)2> User switch command --> r kai2@centos <- r で別のリモートシェルに切り替える --> c Eshell V5.7.1 (abort with ^G) (kai2@centos)1> q(). <- データの再配置が終わるまで、数秒待ってから停止 ok (kai2@centos)2> User switch command --> r kai3@mbp --> c Eshell V5.7.1 (abort with ^G) (kai3@centos)1> q(). ok (kai3@centos)2> User switch command --> q
これで、
では、
$ perl /path/to/kai_test_cluster_get456.pl 1..3 ok 1 ok 2 ok 3
このような実行結果となれば、
Quorum(定足数)ベースプロトコル
本連載では "Quorum" という言葉を何度か用いましたが、
なお、
Quorumを一言で説明すると、
なお、
Kaiは、
実際のサービス運用でQuorumの設定値を決定する際は、
Quorum | 考慮する内容 |
---|---|
N | 常にデータの複製をN個、 ただし、 大きな値を設定すると、 |
R | 大きな値を設定すると、 逆に、 |
W | 大きな値を設定すると、 逆に、 また、 |
表中にあるQuorumの条件
Write/
例えば、
以下の図は、

N:R:W = 3:1:3である場合も、

N:R:W = 3:1:2である場合、

N:R:W = 3:3:1である場合、
ただし、
なお、

KaiのQuorumのデフォルト値は、
- データの喪失を避けるため、
複数のノードにデータの複製を保持する (N > 1) - 書き込みのレスポンス速度を向上させる
(N > W) - 読み込みのレスポンス速度を向上させる
(N > R) - (1の条件) - (3の条件)
を満たす最小のN (N = 3)
なお、
何か特別な事情が無い限りは、
Write/Write Conflictについての蛇足
前述の通り、
ここで述べる事は、
では、
何らかの理由によりクラスタが分裂している際に、
まず始めに、
[{kai, [
{rpc_port, 11011},
{rpc_max_processes, 30},
{memcache_port, 11211},
{memcache_max_processes, 10},
{max_connections, 32},
{n, 3},
{r, 3},
{w, 1},
{number_of_buckets, 1024},
{number_of_virtual_nodes, 128},
{store, ets},
]}].
[{kai, [
{rpc_port, 11012},
{rpc_max_processes, 30},
{memcache_port, 11212},
{memcache_max_processes, 10},
{max_connections, 32},
{n, 3},
{r, 3},
{w, 1},
{number_of_buckets, 1024},
{number_of_virtual_nodes, 128},
{store, ets},
]}].
[{kai, [
{rpc_port, 11013},
{rpc_max_processes, 30},
{memcache_port, 11213},
{memcache_max_processes, 10},
{max_connections, 32},
{n, 3},
{r, 3},
{w, 1},
{number_of_buckets, 1024},
{number_of_virtual_nodes, 128},
{store, ets},
]}].
N:R:W = 3:3:1であるため、
次に、
$ /path/to/start_kai.sh kai_wwc1 kai_wwc2 kai_wwc3
次のようなPerl Scriptを用意し、
#!/usr/bin/env perl
use strict;
use warnings;
use Cache::Memcached;
for (1..3) {
my $mem = Cache::Memcached->new({
servers => ['127.0.0.1:1121' . $_],
});
$mem->set(foo => 'bar' . $_, 0,);
}
W=1であるため、
$ perl /path/to/kai_test_wwc_set.pl
これで、
次に、
$ /path/to/remsh.sh kai1_wwc1 Erlang R13B (erts-5.7.1) [source] [64-bit] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:false] Eshell V5.7.1 (abort with ^G) (kai1_wwc1@centos)1> kai_rpc:check_node({{127,0,0,1}, 11011}, {{127,0,0,1}, 11012}). ok (kai1_wwc1@centos)2> kai_rpc:check_node({{127,0,0,1}, 11012}, {{127,0,0,1}, 11013}). ok (kai1_wwc1@centos)3> kai_rpc:check_node({{127,0,0,1}, 11013}, {{127,0,0,1}, 11011}). ok (kai1_wwc1@centos)4> User switch command --> q
では、
$ telnet localhost 11211 Trying 127.0.0.1... Connected to localhost.localdomain (127.0.0.1). Escape character is '^]'. get foo VALUE foo 0 4 bar1 VALUE foo 0 4 bar2 VALUE foo 0 4 bar3 END quit Connection closed by foreign host.
ご覧の通り、
ここでは、
一般的なmemcachedクライアントは、
前述の通り、
よって、
以下は、
言語 | クライアント | バージョン | 実装 | 戻り値 |
---|---|---|---|---|
Perl | Cache::Memcached | 1. | Pure Perl | 最後の値 |
Cache::Memcached::Fast | 0. | XS(独自実装) | 最初の値 | |
Cache::Memcached::libmemcached | 0. | libmemcached | 最後の値 | |
Ruby | memcache-client | 1. | Pure Ruby | 最後の値 |
Ruby-MemCache | 0. | Pure Ruby | 最後の値 | |
Python | python-memcached | 1. | Pure Python | 最初の値 |
PerlのCache::Memcached、
#!/usr/bin/env python
import memcache
mc = memcache.Client(['127.0.0.1:11211'])
print mc.get('foo')
mc.set('foo_py', 'baz')
print mc.get('foo_py')
$ /path/to/kai_test_wwc_get.py
bar1
bar3 <- foo_py を取得しているにも関わらず、foo の値を取得している
意図的に発生させなければ、
ただ、
Write/