okuyamaのSerializeMapを検証してみた

昨日@kumagi さんと@muga_nishizawa さんとtwitter上でやり取り(http://togetter.com/li/149955)をした時に話題に出した
次のokuyamaに入れようと思っている機能の一つのSerializeMapですが、
あの時、性能の話題になったのでまだ正式にはリリース前なんですが
性能を簡単に測定してみました。
実装したタイミングで簡単には試していたのですが、自分の備忘録代わりに再テストの記録。


そもそもこのSerializeMapの仕組みですが、このMapは内部で1つだけMapオブジェクトを
持っていてこのMapはあらかじめ決められた要素数しかKeyとValueのセットを持たないようになっています。
そして全ての要素はIntegerオブジェクトをKeyとして、ValueシリアライズされたMapのバイナリ
配列をもちます。このバイナリ配列化されたMapを逐次、デシリアライズてそこに値をつめて、
またシリアライズして大本のMapに登録し直します。決められた要素のどこのMapに入るかは
登録時のKey値のHash値を使って素数で割ってその余りの場所に格納しています。この大本Mapの
素数は引数で渡して調整出来るようになっています。
狙いはメモリ中に存在しているMapオブジェクトの要素数を増やさないことです。
要素内のシリアライズされたバイトデータは増えますが、要素数そのものが増えることに
よるオブジェクトの構成要素が占めるメモリ中の容量よりもマシではないかと思い実装しました。


取り合えず測定の視点は、単純に秒間に実行できる性能と、メモリに対してどれぐらい保持できるかです。
参考としてConcurrentHashMapと比べてみました。


以下はSerializeMapのソース
http://sourceforge.jp/projects/okuyama/svn/view/trunk/src/okuyama/imdst/util/serializemap/SerializeMap.java?view=markup&revision=671&root=okuyama


以下はテストに利用したソース
http://sourceforge.jp/projects/okuyama/svn/view/trunk/test/SerializeMapTest.java?view=markup&revision=675&root=okuyama




テストは簡単で、テストスクリプト内で値をSetするスレッドを複数起こして、
それぞれがユニークな値をSetするというもの
このSetを一定時間繰り返してその間にSet出来た回数を実行秒数で割って秒間処理数を、
保存できる最大はOutOfMemoryが出るまで実行してメモリ当たりの最大数を見てみました。
実行回数確認時は各スレッド単位でユニークな値を200万種類の中からランダムに登録し続けたので、
新規、更新入り乱れてると思います。


実行PCのスペックは以下
マシン:ドスパラデスクトップPC
CPU:Core i5 3.2GhHz 物理2コア、仮想4コア
メモリ:4GB
OS:CentOS 5.4 64bit
JAVA:Sun Java1.6.0_25 64bit


JVMオプションは以下にしました。

  • XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseParNewGC

メモリ割当は秒間当たりの処理数を調べる時は-Xmx2048m -Xms2048mで
最大数を調べるときは時間短縮のため、-Xmx512m -Xms512mで試しました。




ではまず1秒当たりのSetの数です。
同時にSetを実行するスレッド数は30です。
*ConcurrentHashMap
775030 QPS


*SerializeMap
62166 QPS


ConcurrentHashMap速い!!
77万とかいくんですね。
対して、SerializeMapは6万なので、12分の1位でしょうか。






ではつぎに、限界格納数
同時にSetを実行するスレッド数は8です。
*ConcurrentHashMap
197万6128件でOutOfMemory


*SerializeMap
2100万以上1時間半ぐらい動いた時点で止めました。


だいたい、同じメモリ量で、11倍以上格納出来ています。
格納のスピードはというと、ConcurrentHashMapは止まるまで
秒間当たり、ほぼ同じスピードでSetし続けたのに対して、
SerializeMapは以下のようになりました。
以下のログは3秒間隔でその時点のMapのsizeを出しています。


開始直後
1108159
1263967
1413241
1563692
1729365
大体、3秒で15万から16万件のペースでsizeが増えているので、
秒間当たり、5万Setできているのが分かります。


そして、1000万を超えた当たりでは、
10046581
10142857
10227589
10321581
10405751
大体、3秒で8万件から10万件程度増えているので、秒当たり、3万Setぐらでしょうか。


そして、2000万を超えたあたりでは、
19999741
20002336
20004813
20007616
20009919
大体、3秒で2000件から3000件程度増えているので、秒当たり、900Setぐらでしょうか。




内部でシリアライズ、デシリアラズを常に行い、さらに圧縮処理を行っているので、データが多くなれば
それだけ遅くなっているようです。
スピードは秒間1万Set程度でいいから大量の値を持ちたいよって時に、使えるかな。
okuyamaは内部でこれを使っているので、周りにネットワーク処理とか同期処理とか色々入るので、
この生の性能は出ませんが、データファイルの位置記録用には結構重宝しそうです。


次はGetも検証してみたいと思います。

okuyamaプロトコル仕様表

okuaymaのオリジナルプロトコルの仕様を表にまとめした。
からり表が大きく見にくくなってしまいました...orz
以降okuyama側に変更があった場合もこの表をメンテナンスしてきます。


事前説明ですが、
1.各要素間のセパレータは","になります。
2.Encode指定という部分はBase64でのEncode指定を意味します。
  送信側でEncodeで○となっている部分はBase64エンコードしてから送信することを意味し、
  返却側はサーバからEncodeされた値が返されることを意味しますので、Decodeして使用してください。
3.追記
  2010/01/03:肝心なことを書き忘れてました。プロトコルの終端の区切りは"\r\n"です。
  2010/01/04:Valueにブランク(サイズ0の値)を指定したい場合はBase64エンコードをせずに"(B)"という文字列を指定してください。
                      okuyama側でブランクとして扱われます。
  2010/01/04:Key値にブランク(サイズ0の値)は指定できません。エラーとしてください。
  2010/01/04:Tag値に"(B)"を指定した場合は、okuyama側でTag指定なしとして処理されます。
  2011/06/06:incrValue、decrValue、getMultiValue、getTagValues、removeTagFromKey、setValueAndCreateIndex、searchValueのプロトコルを追記しました。


ソッド名



処理内容



サー
バから返却される値


第2
要素
第3
要素
第4
要素
第5
要素
第6
要素
第1

要素
第2
要素
第3
要素
第4
要素
initClient



保存可能な最大サイズをMasterNodeへ問い合わせる












0 true 数値

Valueのbyte長


Encode























0,true,1048576
setValue



値を保存する(Tagの有り無しで転送の第3要素が変化する)


Key値 Tag値

Tagを指定しない場合は"(B)"を連結する。存在する場合はTag文字列を":"をセパレータに連結する
分散ロック値

"0"固定
Value

1 "true" or "false" or
"error"

正常に保存出来た場合は"true".なんだかの理由により論理的(サイズオーバーなど)に保存できない場合は"false".サーバ側でエラーの場合は
"error"
"OK" or メッセージ

返却第2要素が"true"の場合は"OK"になる."false"や、"error"の場合はメッセージが格納される


Encode

(B)の場合は×

Tagが存在する場合は1Tag単位でエンコード
×

× × ×




例1正常保存)
  1,true,OK

例2Value値サイズオーバー)  1,false,Value Length Error

getValue



Keyを指定して値を取得


Key値







2 "true" or "false" or
"error"

値が取得出来た場合は"true".取得できない、なんだかの理由により論理的(サイズオーバーなど)に取得できない場合は"false".サーバ側でエ
ラーの場合は"error"
Value値 or メッセージ

返却第2要素が"true"の場合はValueになる."false"や、"error"の場合はメッセージが格納される


Encode









× × ○ or ×

返却値の第2要素が"true"の場合は○.それ意外は×





例1値あり)
  2,true,dmFsdWUx

例2値なし)  2,false,

例3Key値サイズオーバー)  2,false,Key Length Error

getTagKeys



Tagを指定することでKey値を取得する


Tag値 "true" or
"false"

削除済みのKey-Valueセットの扱い.trueを指定すると、削除済みでもKey値は返される

falseを指定すると、削除済みのKey値は返されない.






4 "true" or "false" or
"error"

値が取得出来た場合は"true".取得できない、なんだかの理由により論理的(サイズオーバーなど)に取得できない場合は"false".サーバ側でエ
ラーの場合は"error"
Value値 or メッセージ

返却第2要素が"true"の場合は同じTagが打たれているKey値の連結文字列になるになる.セパレータは":"

"false"や、"error"の場合はメッセージが格納される


Encode

×





× × ○ or ×

返却値の第2要素が"true"の場合は○.それ意外は×





例1Key値あり)
  4,true,dGFnc2FtcGxlZGF0YWtleV8w:a2V5MQ==:a2V5Mw==

例2値なし)  4,false,

例3Tag値サイズオーバー)  4,false,Tag Length Error

removeValue



Key値を指定することでKey-Valueセットを削除する


Key値 分散ロック値

"0"固定






5 "true" or "false" or
"error"

値が削除出来た場合は"true".取得できない、なんだかの理由により論理的(サイズオーバーなど)に取得できない場合は"false".サーバ側でエ
ラーの場合は"error"
Value値 or メッセージ

返却第2要素が"true"の場合は削除対象のValue

"false"や、"error"の場合はメッセージが格納される


Encode

×





× × ○ or ×

返却値の第2要素が"true"の場合は○.それ意外は×





例1Key値あり)
  5,true,c2F2ZWRhdGF2YWx1ZXN0cl8w

例2値なし)  5,false,

例3Key値サイズオーバー)  5,false,Key Length Error

setNewValue



値を保存する(Tagの有り無しで転送の第3要素が変化する)

既に同値のKeyがサーバに存在する場合は失敗する

memcachedの"add"命令に相当する


Key値 Tag値

Tagを指定しない場合は"(B)"を連結する。存在する場合はTag文字列を":"をセパレータに連結する
分散ロック値

"0"固定
Value

6 "true" or "false" or
"error"

正常に保存出来た場合は"true".なんだかの理由により論理的(既にKeyが存在する.サイズオーバーなど)に保存できない場合は"false".
サーバ側でエラーの場合は"error"
"OK" or メッセージ

返却第2要素が"true"の場合は"OK"になる."false"や、"error"の場合はメッセージが格納される


Encode

(B)の場合は×

Tagが存在する場合は1Tag単位でエンコード
×

× × ×




例1成功)
  6,true,OK

例2失敗)  6,false,NG:Data has already been registered

getValueVersionCheck



値を取得すると同時に該当Key-ValueセットのVersionNoも返す

memcachedの"gets"命令に相当する


Key値








15 "true" or "false" or
"error"

値が取得出来た場合は"true".取得できない、なんだかの理由により論理的(サイズオーバーなど)に取得できない場合は"false".サーバ側でエ
ラーの場合は"error"
Value値 or メッセージ

返却第2要素が"true"の場合はValueになる."false"や、"error"の場合はメッセージが格納される
VersionNo値(数値)
Encode









× × ×



例1取得成功)
  15,true,dmFsdWUx,0

例2取得成功VersionNo値が"2")  15,true,dmFsdWUx,2

例3値なし)  15,false,,

setValueVersionCheck



Key-Valueのセットを更新する.この際に、VersionNoを同時に渡し、VersionNoがサーバ側で変更されていない場合のみ処理が成功
する

memcachedの"cas"命令に相当する


Key値 Tag値

Tagを指定しない場合は"(B)"を連結する。存在する場合はTag文字列を":"をセパレータに連結する
分散ロック値

"0"固定
Value VersionNo値

getValueVersionCheckで取得した値
16 "true" or "false" or
"error"

正しく更新出来た場合は"true".更新出来ない、なんだかの理由により論理的(サイズオーバーなど)に取得できない場合は"false".サーバ側で
エラーの場合は"error"
"OK" or メッセージ

返却第2要素が"true"の場合は"OK"になる."false"や、"error"の場合はメッセージが格納される


Encode

(B)の場合は×

Tagが存在する場合は1Tag単位でエンコード
× × × ×





例1更新成功)
  16,true,OK

例2更新失敗)  16,false,NG:Data has already been updated
incrValue



数値を加算処理を行う。

加算する対象のKey-Valueはあらかじめ登録しておく必要がある。

登録済みのValueが数値ではない

Key-Valueセットに実行すると、Valueが0に初期化される。

memcachedの"incr"命令に相当する



Key値 分散ロック値

"0"固定
Value

(数値のみ)





13 "true" or "false"

正しく更新出来た場合は"true".更新出来ない、なんだかの理由により論理的(サイズオーバーなど)に更新出来ない場合は"false"

演算結果or メッセージ

返却第2要素が"true"の場合は加算後の結果数値になる."false"の場合はメッセージが格納される


Encode

×



× ×





例1更新成功)
  13,true,Nw=

例2更新失敗)  13,false,NG

decrValue


数値を減算処理を行う。

減算する対象のKey-Valueはあらかじめ登録しておく必要がある。

登録済みのValueが数値ではない

Key-Valueセットに実行すると、Valueが0に初期化される。

0よりも小さい値の減算は出来ない。

つまり0に減算を行っても0のままとなる

memcachedの"decr"命令に相当する



Key値 分散ロック値

"0"固定

Value

(数値のみ)





14 "true" or "false"

正しく更新出来た場合は"true".更新出来ない、なんだかの理由により論理的(サイズオーバーなど)に更新出来ない場合は"false"

演算結果or メッセージ

返却第2要素が"true"の場合は加算後の結果数値になる."false"の場合はメッセージが格納される




Encode

×





× ×







例1更新成功)
  14,true,Nw=

例2更新失敗)  14,false,NG

getMultiValue


Keyを複数指定して値を取得

memcachedのgetコマンドにKey値を

複数個並べて一度に取得する

命令(GetMulti)に相当




Key値

(カンマ区切りで取得したい

Key値を複数指定)













22 or

"END"








"true" or "false"

値が取得出来た場合は"true".取得できない、なんだかの理由により論理的(サイズオーバーなど)に取得できない場合は"false"


指定したKey値の順番にValue

改行区切りで返される。


Value

返却第2要素が"true"の場合はValueになる."false"の場合はこの値はない




Encode













× ×


-


(特記事項)

上記の返却文字列は1つのKeyとValueのセット単位で改行(LF)区切りで返される

つまり改行(LF)区切りでNetworkから読み出し、その単位が送信したKey値の順番に

紐付く結果となる。そして最後に"END"という文字列が終了を意味するマーカー

として返却され処理は終了となる。







例1)
  22,true,dmFsdWUxMA==

          22,false,

         
22,true,dmFsdWUxMg==

         
22,true,dmFsdWUxMw==

          END

※上記は"key12"はデータとして存在しない場合の返却例である。


getTagValues


Tagを指定して、そのTagが登録されている、KeyとValueのセットを取得する。

挙動としてはgetMultiValueと非常ににているが、返却される値が、Keyと

Valueの連結文字列になっている部分が異なる。




Tag値

(複数指定は不可)











23 or

"END"



"true"


エンコードされたKey値

エンコードされたValue
Encode













× ×
-

(特記事項)

上記の返却文字列は1つ紐付くのデータ単位で改行(LF)区切りで返される

つまり改行(LF)区切りでNetworkから読み出し、その単位デコードを行い処理する。

返却される第3要素はKey値をBase64でエンコードした値となる

返却される第4要素は第3要素のKey値に紐付くValue値をBase64でエンコードした

値となるそして最後に"END"という文字列が終了を意味するマーカーとして返却され

処理は終了となる。




例1)
  23,true,dGFnc2FtcGxlZGF0YWtleV80,dGFnc2FtcGxlc2F2ZWRhdGFfNA==

         
23,true,dGFnc2FtcGxlZGF0YWtleV8w,dGFnc2FtcGxlc2F2ZWRhdGFfMA==

         
23,true,dGFnc2FtcGxlZGF0YWtleV8x,dGFnc2FtcGxlc2F2ZWRhdGFfMQ==

         
23,true,dGFnc2FtcGxlZGF0YWtleV8y,dGFnc2FtcGxlc2F2ZWRhdGFfMg==

          END

removeTagFromKey


Tag値とそのTag値に紐付くをKey指定することでTagとの紐付きを削
除できる。



Tag値 Key値 分散ロック値

"0"固定







40


"true" or "false" or
"error"

値が削除出来た場合は"true"。論理的理由指定のKey値とTag値の紐付きが無いなど)に
より削除できない場合は"false".サーバ側でエ
ラーの場合は"error"

返却第2要素が"true"の場合はなし

"false"や、"error"の場合はメッセージが格納される




Encode

×





× × ×







例1値あり)
  40,true,

例2値なし)  40,false,

例3Key値サイズオーバー)  40,false,Key Length Error

setValueAndCreateIndex


値を保存する

保存と同時に全文検索用の検索Indexを

作成する。

作成時の解析方式は、N-Gram

作成するIndexのグルーピィングや、N-GramのNの長さを指定できる。

デフォルトの挙動では1(ユニグラム)、2(バイグラム)、3(トリグラム)文字でインデックスが

作成される。




Key値 Tag値

Tagを指定しない場合は"(B)"を連結する。存在する場合はTag文字列を":"をセパレータに連結する

分散ロック値

"0"固定

Value
第6要素


第7要素


第8要素


グループ名

作成される検索Indexが

所属する

グループ名

グループ指定しない場合は、

"(B)"を連結













Indexの最短の長さ


数字指定

デフォルト

"1"を指定すること


Indexの最長の長さ


数字指定

デフォルト"3"を指定

すること

※表を拡張するのが大変なのでまとめて

ここにかきました。


42


"true" or "false" or
"error"

正常に保存出来た場合は"true".なんだかの理由により論理的(サイズオーバーなど)に保存できない場合は"false".サーバ側でエラーの場合は
"error"

"OK" or メッセージ

返却第2要素が"true"の場合は"OK"になる."false"や、"error"の場合はメッセージが格納される




Encode

(B)の場合は×

Tagが存在する場合は1Tag単位でエンコード

×


(B)の場合は×



×        


×       
※表を拡張するのが大変なのでまとめて

ここにかきました。

× × ×







例1正常保存)
  42,true,OK

例2Value値サイズオーバー)  42,false,Value Length Error

searchValue


検索したいWordを指定して、setValueAndCreateIndexで登録した

Valueに文字検索を行う

検索Indexを限定するグループ指定や、

検索時に検索WordをN-Gram方式で分解

する長さを指定できる。



検索

文字列群


複数指定する場合は文字列を":"をセパレータに連結する



複数検索条件の挙動

(AND or OR)

"1"=AND

"2"=OR

検索対象Indexのグルー
プ名



グループなし

okuyamaプロトコル仕様表

okuaymaのオリジナルプロトコルの仕様を表にまとめした。
からり表が大きく見にくくなってしまいました...orz
以降okuyama側に変更があった場合もこの表をメンテナンスしてきます。


事前説明ですが、
1.各要素間のセパレータは","になります。
2.Encode指定という部分はBase64でのEncode指定を意味します。
  送信側でEncodeで○となっている部分はBase64エンコードしてから送信することを意味し、
  返却側はサーバからEncodeされた値が返されることを意味しますので、Decodeして使用してください。
3.追記
  2010/01/03:肝心なことを書き忘れてました。プロトコルの終端の区切りは"\r\n"です。
  2010/01/04:Valueにブランク(サイズ0の値)を指定したい場合はBase64エンコードをせずに"(B)"という文字列を指定してください。
                      okuyama側でブランクとして扱われます。
  2010/01/04:Key値にブランク(サイズ0の値)は指定できません。エラーとしてください。
  2010/01/04:Tag値に"(B)"を指定した場合は、okuyama側でTag指定なしとして処理されます。

メソッド名

処理内容
説明 サーバへ転送する値 サーバから返却される値
第1
要素
第2要素 第3要素 第4要素 第5要素 第6要素 第1
要素
第2要素 第3要素 第4要素
initClient

保存可能な最大サイズをMasterNodeへ問い合わせる
0















0
true
数値
Valueのbyte長




Encode ×





























0 0,true,1048576
setValue

値を保存する(Tagの有り無しで転送の第3要素が変化する)
1 Key値 Tag値
Tagを指定しない場合は"(B)"を連結する。存在する場合はTag文字列を":"をセパレータに連結する
分散ロック値
"0"固定
Value

1 "true" or "false" or "error"
正常に保存出来た場合は"true".なんだかの理由により論理的(サイズオーバーなど)に保存できない場合は"false".サーバ側でエラーの場合は"error"
"OK" or メッセージ
返却第2要素が"true"の場合は"OK"になる."false"や、"error"の場合はメッセージが格納される


Encode ×

(B)の場合は×
Tagが存在する場合は1Tag単位でエンコード

×




×
×
×





例1Tagなし)  1,ZGF0YXNhdmVrZXlfMA==,(B),0,c2F2ZWRhdGF2YWx1ZXN0cl8w

例2Tagあり)  1,a2V5MQ==,dGFnMQ==:dGFnMg==,0,dmFsdWUx
例1正常保存)  1,true,OK
例2Value値サイズオーバー)  1,false,Value Length Error
getValue

Keyを指定して値を取得
2 Key値







2 "true" or "false" or "error"
値が取得出来た場合は"true".取得できない、なんだかの理由により論理的(サイズオーバーなど)に取得できない場合は"false".サーバ側でエラーの場合は"error"
Value値 or メッセージ
返却第2要素が"true"の場合はValueになる."false"や、"error"の場合はメッセージが格納される


Encode ×













×
×
○ or ×
返却値の第2要素が"true"の場合は○.それ意外は×






2,a2V5MQ== 例1値あり)  2,true,dmFsdWUx
例2値なし)  2,false,
例3Key値サイズオーバー)  2,false,Key Length Error
getTagKeys

Tagを指定することでKey値を取得する
3 Tag値 "true" or "false"
削除済みのKey-Valueセットの扱い.trueを指定すると、削除済みでもKey値は返される
falseを指定すると、削除済みのKey値は返されない.






4 "true" or "false" or "error"
値が取得出来た場合は"true".取得できない、なんだかの理由により論理的(サイズオーバーなど)に取得できない場合は"false".サーバ側でエラーの場合は"error"
Value値 or メッセージ
返却第2要素が"true"の場合は同じTagが打たれているKey値の連結文字列になるになる.セパレータは":"
"false"や、"error"の場合はメッセージが格納される


Encode ×

×









×
×
○ or ×
返却値の第2要素が"true"の場合は○.それ意外は×






3,dGFnMQ==,true 例1Key値あり)  4,true,dGFnc2FtcGxlZGF0YWtleV8w:a2V5MQ==:a2V5Mw==
例2値なし)  4,false,
例3Tag値サイズオーバー)  4,false,Tag Length Error
removeValue

Key値を指定することでKey-Valueセットを削除する
5 Key値 分散ロック値
"0"固定






5 "true" or "false" or "error"
値が削除出来た場合は"true".取得できない、なんだかの理由により論理的(サイズオーバーなど)に取得できない場合は"false".サーバ側でエラーの場合は"error"

Value値 or メッセージ
返却第2要素が"true"の場合は削除対象のValue
"false"や、"error"の場合はメッセージが格納される


Encode ×

×









×
×
○ or ×
返却値の第2要素が"true"の場合は○.それ意外は×






5,ZGF0YXNhdmVrZXlfMA==,0 例1Key値あり)  5,true,c2F2ZWRhdGF2YWx1ZXN0cl8w
例2値なし)  5,false,
例3Key値サイズオーバー)  5,false,Key Length Error
setNewValue

値を保存する(Tagの有り無しで転送の第3要素が変化する)
既に同値のKeyがサーバに存在する場合は失敗する
memcachedの"add"命令に相当する
6 Key値 Tag値
Tagを指定しない場合は"(B)"を連結する。存在する場合はTag文字列を":"をセパレータに連結する
分散ロック値
"0"固定
Value

6 "true" or "false" or "error"
正常に保存出来た場合は"true".なんだかの理由により論理的(既にKeyが存在する.サイズオーバーなど)に保存できない場合は"false".サーバ側でエラーの場合は"error"
"OK" or メッセージ
返却第2要素が"true"の場合は"OK"になる."false"や、"error"の場合はメッセージが格納される



Encode ×

(B)の場合は×
Tagが存在する場合は1Tag単位でエンコード

×




×
×
×





例1Tagなし)  6,a2V5MQ==,(B),0,dmFsdWUx
例2Tagあり)  6,a2V5Mw==,dGFnMQ==:dGFnMg==:dGFnMw==,0,dmFsdWUz
例1成功)  6,true,OK
例2失敗)  6,false,NG:Data has already been registered
getValueVersionCheck

値を取得すると同時に該当Key-ValueセットのVersionNoも返す
memcachedの"gets"命令に相当する
15
Key値










15
"true" or "false" or "error"
値が取得出来た場合は"true".取得できない、なんだかの理由により論理的(サイズオーバーなど)に取得できない場合は"false".サーバ側でエラーの場合は"error"

Value値 or メッセージ
返却第2要素が"true"の場合はValueになる."false"や、"error"の場合はメッセージが格納される
VersionNo値(数値)
Encode ×













×
×

×


例1)  15,a2V5MQ==
例1取得成功)  15,true,dmFsdWUx,0
例2取得成功VersionNo値が"2")  15,true,dmFsdWUx,2
例3値なし)  15,false,,
setValueVersionCheck

Key-Valueのセットを更新する.この際に、VersionNoを同時に渡し、VersionNoがサーバ側で変更されていない場合のみ処理が成功する
memcachedの"cas"命令に相当する
16
Key値 Tag値
Tagを指定しない場合は"(B)"を連結する。存在する場合はTag文字列を":"をセパレータに連結する
分散ロック値
"0"固定
Value VersionNo値
getValueVersionCheckで取得した値
16
"true" or "false" or "error"
正しく更新出来た場合は"true".更新出来ない、なんだかの理由により論理的(サイズオーバーなど)に取得できない場合は"false".サーバ側でエラーの場合は"error"

"OK" or メッセージ
返却第2要素が"true"の場合は"OK"になる."false"や、"error"の場合はメッセージが格納される



Encode ×

(B)の場合は×
Tagが存在する場合は1Tag単位でエンコード

×

×
×
×






例1Tagなし)  16,a2V5MQ==,(B),0,dmFsdWUx,1
例2Tagあり)  16,a2V5MQ==,dGFnMQ==:dGFnMg==:dGFnOTk=,0,dmFsdWUx,3
例1更新成功)  16,true,OK
例2更新失敗)  16,false,NG:Data has already been updated

okuyama-0.8.4リリース、0.8.5のことも。そして次の何か。。

また大分さぼってしまった。。

ということで、okuyama-0.8.4をリリースしました。
0.8.4のことの前にここには、0.8.0以降のことを何も記していなので、
追加した機能をチョット軽く整理すると、




■0.8.1
    ・トランザクションログファイルを一定サイズで自動的にローテーションするように変更
    ・JavaScriptでデータ更新も行えるように変更
    ・Valueがファイルモード時にファイルから一度読み出しValueの一定量をメモリにキャッシュするように機能追加
      (LRUアルゴリズムで使用しないものからパージされます)
    ・データノードのメモリ活用方法を効率化し、従来よりも大量のデータをメモリ保持できるように修正
      データノード内でのKey値探索方式に工夫を行い、取得効率を向上


■0.8.2
    ・ant用のbuild.xmlを作成してantから起動できるようにした
    ・不具合対応全般


■0.8.3
    ・ストレージモードに完全ディスクモードを追加
    ・パッケージ構造は大幅に変更
    ・TagからKeyを取得する際にすでに削除されたKey値を返さないOption引数を追加
    ・Tag登録時のロックアルゴリズムを見直しによる処理性能の向上
    ・不具合対応全般




上記が0.8.1から0.8.3の流れです。
大きくはデータストレージ周りと、処理性能の向上にフォーカスしてやっていた感じです。
そしてこの期間中に
「NoSQL afternoon in Japan(http://atnd.org/events/8460)」への登壇や
Java Cloud Meeting in Kansai」への登壇などを経験させていただきました。
色々情報収集できてかなり有意義な時間を過ごせました。


そして0.8.4なんですが、以下の機能を追加しました。
■0.8.4
    ・データ有効期限を設定できる機能を追加
      memcachedでいうところのexpireTimeです。memcachedと同じ挙動になります。
    ・排他的更新機能を追加(gets & cas)
      memcachedでいうところのgetsとcasへの対応です。これもmemcachedと同じ挙動になります。
    ・VirtualMemory機能の実装
      これはokuyamaが稼動中にメモリが足りなくなったさいに従来はOutOfMemoryが発生していたいのですが、
      自動的にディスクにKey-Valueの一部を書き出して、OutOfMemoryが発生しないように対応したというものです。
    ・Valueをディスクに保持するモード時のディスク使用率を効率化
      これは従来Valueをファイルに保存するモードを選択した際は、1Valueは必ず固定長でファイルに書き出されていたのですが、
      これではディスクの使用効率が悪いので、1Valueの一定長までは全Value共有のファイルに、それ以上は個別の1Value単位での
      専用ファイルに保存するようにしました。これで一定長以内のValueは共有ファイルに保存されるので、一部の大きなValue
      対応するためにファイルを無駄な固定長で埋め尽くさずにすむようになります。
    ・完全ディスクモード時にディスクへの書く込み処理を非同期に変更
      完全ディスクモードはディスクの容量一杯までデータを保存できますが、やはり書き込みが遅いので、非同期書き込みを
      実装しました。正しこれはKeyファイルとValueファイルだけのことで、データの永続性を実現している
      トランザクションファイルへの書き込みは完了してからClientへ応答するので、永続性は保障されます。
    ・ネットワーク越しに停止できるように変更
      ネットワーク経由で停止コマンドを送信できるように機能追加
    ・不具合対応全般


一番大きな機能追加はcasへの対応でしょうか。
これは「第3回クラウド勉強会「〜NoSQL(KVS)大集合〜」」への登壇が決まっていたため、
その前に絶対実装しておきたかったので、必達目標でした。
出来てよかった〜。。。


後はやはりストレージ周りが大きいですね。
仮想メモリとかディスク使用量の効率化などです。




そして、0.8.5ですが、まずはexpireTimeで有効期限切れになったデータを自動的に
物理削除するようにします。
okuyamaは永続性を前面に押し出していることもあり、メモリ使用量やディスク使用量が
あふれそうな場合に自動でパージする機能は無かったのですが、今回の有効期限の
対応に合わせて実装しました。後は、Tag周りの処理性能向上は行う予定です。






そして次の何かなんですが、以下のプロジェクトを立ち上げました。
「分散ストレージ GalleryEs」http://sourceforge.jp/projects/galleryes/
分散ストレージに取り掛かることにしました。okuyamaというメタサーバもできたので、
本格的に進めようか思います。


まあまだ何にもないですが。。。。




年末頑張るかなー。

書くこと一杯。。取り合えずokuyama-0.8.0のことから

相当ここをサボってしまいました。
書くこと一杯あるんですが(gumiさん勉強会でしゃべりましたとか、OSC東京とか)、
取り合えずokuyamaの0.8.0のことを書きます。




前回0.7.0をリリースしてから2ヶ月半以上開発したのですが、それまでは
もっと短期間にリリースを繰り返していたのですが、今回はすこし時間をかけました。


一番時間を使ったのはネットワークI/O部分。
従来okuyamaは接続がある都度スレッドを生成して1対1の通信をクライアント、マスターノード、
データノード間で行っていました。
これは少ない同時接続数(100とか200程度)の間は特に問題なくレスポンスも出ていたのですが、
同時接続数が増えるに従って動作不良を招きます。
当然スレッド数に依存するので、同時接続1万とか無理です。
これは何とかせねばということで改造にとりかかりました。


でも実は自宅のAMDの2Coreのデスクトップではもっと顕著で、このマシンでokuyamaの
全ノードを動かしている状態で2クライアントでget、setを繰り返している状態で、
3クライアント目は接続できませんでした。2クライアントでCPUを100%使い果たしてしまい、
3クライアント目のスレッドが生成できなかったんです。


そして試したのが、Javaのnioです。
従来はただのioを使用していたので、ブロッキングI/Oです。
これだとクライアントが通信を始めてなくても、コネクトがあった瞬間に
スレッドが待ちになるので、よろしくないです。
nioはなにか通信イベントが発生するたびに、通知してくれます。
もうこれだなってことでテスト実装したんですが、最終的に思ったほど
性能がでませんでした。同時接続数1万且つ、接続完了クライアントから、
処理開始をテストしていたんですが、僕の実装がいけてなかったんだと思います。。


それで良くないとわかっていたのですが、ioを使用して多段式のI/O受付を
実装しました。クライアントからの通信状況を監視するスレッドと、実際に
処理を行うスレッドを分割し、さらにこれにQueueを使用して並列化しました。
これで一応1万クライアントで同時接続して処理を行うことができました。
ただ、やはり1万同時接続時は性能が低下するので、まだ改良の余地はあるかと。
その前にnioに再チャレンジするのも手かもしれませんが。


後は、ConsistentHashをサポートしたり、データノードのレプリケーション数を
3ノードに出来るようにしたり、取得時のデータ一貫性をサポートしたりと、
色々いじりました。




最低限入れたい機能は取り合えず入ったイメージです。
最近okuyamaを色々な場所で紹介させていただいていて
色々ご意見をいただけているので、その声を元に機能拡張して
いこうかと思っています。
次のバージョンで早速その1部を実装してリリースします。

OSC@名古屋に参加してきました

オープンソースカンファレンス@名古屋に参加してきました。
okuyamaのセミナーも実施させていただき、多くの方とお話でき
とても楽しく、勉強になる時間をすごせました。


名古屋のコミュニティーの方々の盛り上がり方は凄いですね!!
圧倒されました。


後、名古屋のほうが関西より少し涼しいんですね。




okuyamaの次期バージョンの実装も大体半分くらいといったとこでしょうか。
弄りだすとあれも、これもと欲が出来てきて。。


次はOSC@東京でセミナーを実施させて頂く予定なので、
そこでは新バージョンの紹介が出来ればと思います。

OSC@Kansai-京都無事終了

オープンソースカンファレンス@Kansai-京都に参加してきました。


初日の1枠目のセミナーで講師を務めさせていただきましたが、
天気の悪い中多くのかたに参加頂きました。ありがとうございました。
冗長化のデモでは片側のサーバの電源を抜くというデモをしたのですが、
TCPタイムアウトを長く設定していた為、止まっちゃいました。
失敗。。。。。
相当あせりました。
TCPタイムアウト時間は帰って速攻調整しました。


セミナー後は自社のブースに戻り来場者様対応。
多く方々にokuyamaへの質問、アドバイスを頂き、
終始貴重な時間を過ごすことが出来ました。
ありがとうございました。
既に幾つかの改修を実施開始しました。




さー次は名古屋か、東京か!!


セミナー資料UPしました。
http://www.slideshare.net/okuyamaoo/20100709-osc-kansaikvsokuyama-4729515