memcachedのオプションにハマる
memcachedにアクセスするためのC言語のAPIには,オプションを設定する関数,memcached_behavior_set()があります.
memcached_behavior_get memcached_behavior_set
オプションの効果を検証しようと思い,ベンチマークをやってみました.
ベースとなるプログラムは,こんな感じ.
#include<libmemcached/memcached.h> #include<stdio.h> #include<string.h> enum mode_type { SET, GET, DEL}; const int TOTAL_CONNECTIONS = 10; int access_memcache(mode_type mode, int seed) { memcached_return rc; memcached_st *memcached; memcached_server_st *memc_servers = NULL; uint32_t flags; char key[] = "keyXXXXX", val[] = "valXXXXX", *return_val; size_t value_length; memc_servers = memcached_server_list_append(memc_servers, "memcachedserver", 11211, &rc); memcached = memcached_create(NULL); rc = memcached_server_push(memcached, memc_servers); if(rc != MEMCACHED_SUCCESS) { printf("memcached_server_push error=[%s]\n",memcached_strerror(memcached, rc)); } /* ここにあとからオプションをセットするコードを書く */ switch(mode) { case SET: snprintf(key,sizeof(key),"key%05d",seed); snprintf(val,sizeof(val),"val%05d",seed); rc = memcached_set(memcached, key, sizeof(key), val, sizeof(val), (size_t)0, (uint32_t)0); if(rc != MEMCACHED_SUCCESS) { printf("memcached_set error=[%s] for KEY[%s] and VALUE[%s]\n", memcached_strerror(memcached, rc), key, val); }else { printf("VALUE[%s] by KEY[%s] was set successfully\n",val, key); } break; case GET: snprintf(key,sizeof(key),"key%05d",seed); return_val = memcached_get(memcached, key, sizeof(key), &value_length, &flags, &rc); if(rc != MEMCACHED_SUCCESS) { printf("memcached_get error=[%s] for KEY[%s]\n", memcached_strerror(memcached, rc),key); }else { printf("VALUE[%s] by KEY[%s] was get successfully\n",return_val, key); } break; case DEL: snprintf(key,sizeof(key),"key%05d",seed); rc = memcached_delete(memcached, key, sizeof(key), (time_t)0); if(rc != MEMCACHED_SUCCESS) { printf("memcached_delete error=[%s] for KEY[%s]\n", memcached_strerror(memcached, rc),key); }else { printf("VALUE by KEY[%s] was deleted successfully\n", key); } break; } memcached_server_list_free(memc_servers); memcached_free(memcached); return(0); } int main(int argc, char *argv[]) { int i = 1; mode_type mode; if(argc != 2) { printf("usage: %s SET|GET|DEL\n", argv[0]); exit(1); }else { if(strcmp(argv[1],"SET")==0) mode = SET; if(strcmp(argv[1],"GET")==0) mode = GET; if(strcmp(argv[1],"DEL")==0) mode = DEL; } for(i=0;i<TOTAL_CONNECTIONS;++i) { access_memcache(mode, i); } return(0); }
コンパイルして,「実行ファイル名 SET」とやると値がセットされ,「実行ファイル名 GET」とするとキーと値のペアが表示され,「実行ファイル名 DEL」で削除してから「実行ファイル名 GET」とすると,NOT FOUNDのエラーが出ると思います.
TOTAL_CONNECTIONSが10くらいだと一瞬で終わってしまうので,試しに10000に設定し,表示は/dev/nullに飛ばしてtimeコマンドで時間を測定.
そしてネットワーク系のパフォーマンスに関係しそうなオプションは,MEMCACHED_BEHAVIOR_NO_BLOCK,MEMCACHED_BEHAVIOR_TCP_NODELAY,MEMCACHED_BEHAVIOR_BUFFER_REQUESTS.
3つのオプションのオン・オフがあるので,全部で8パターンあります.がんばって全パターンをやってみます.
順次,こんな感じでオプションを設定し,プログラムを実行してみます.
rc = memcached_behavior_set (memcached, MEMCACHED_BEHAVIOR_NO_BLOCK, //memcached_behavior flag, 1 // uint64_t data );
それでは,測定スタート.
(MEMCACHED_BEHAVIOR_NO_BLOCK,MEMCACHED_BEHAVIOR_TCP_NODELAY,MEMCACHED_BEHAVIOR_BUFFER_REQUESTS)=(OFF,OFF,OFF) # time ./実行ファイル SET > /dev/null real 0m8.847s # time ./実行ファイル GET > /dev/null real 0m8.877s # time ./実行ファイル DEL > /dev/null real 0m8.819s
(MEMCACHED_BEHAVIOR_NO_BLOCK,MEMCACHED_BEHAVIOR_TCP_NODELAY,MEMCACHED_BEHAVIOR_BUFFER_REQUESTS)=(ON,OFF,OFF) # time ./実行ファイル SET > /dev/null real 0m10.163s # time ./実行ファイル GET > /dev/null real 0m10.226s # time ./実行ファイル DEL > /dev/null real 0m10.173s
(MEMCACHED_BEHAVIOR_NO_BLOCK,MEMCACHED_BEHAVIOR_TCP_NODELAY,MEMCACHED_BEHAVIOR_BUFFER_REQUESTS)=(OFF,ON,OFF) # time ./実行ファイル SET > /dev/null real 0m8.941s # time ./実行ファイル GET > /dev/null real 0m8.861s # time ./実行ファイル DEL > /dev/null real 0m8.899s
(MEMCACHED_BEHAVIOR_NO_BLOCK,MEMCACHED_BEHAVIOR_TCP_NODELAY,MEMCACHED_BEHAVIOR_BUFFER_REQUESTS)=(OFF,OFF,ON) # time ./実行ファイル SET > /dev/null real 0m7.468s # time ./実行ファイル GET > /dev/null real 0m8.956s # time ./実行ファイル DEL > /dev/null real 0m7.595s
(MEMCACHED_BEHAVIOR_NO_BLOCK,MEMCACHED_BEHAVIOR_TCP_NODELAY,MEMCACHED_BEHAVIOR_BUFFER_REQUESTS)=(ON,ON,OFF) # time ./実行ファイル SET > /dev/null real 0m10.250s # time ./実行ファイル GET > /dev/null real 0m10.140s # time ./実行ファイル DEL > /dev/null real 0m10.296s
(MEMCACHED_BEHAVIOR_NO_BLOCK,MEMCACHED_BEHAVIOR_TCP_NODELAY,MEMCACHED_BEHAVIOR_BUFFER_REQUESTS)=(ON,OFF,ON) # time ./実行ファイル SET > /dev/null real 0m8.772s # time ./実行ファイル GET > /dev/null real 0m10.152s # time ./実行ファイル DEL > /dev/null real 0m8.675s
(MEMCACHED_BEHAVIOR_NO_BLOCK,MEMCACHED_BEHAVIOR_TCP_NODELAY,MEMCACHED_BEHAVIOR_BUFFER_REQUESTS)=(OFF,ON,ON) # time ./実行ファイル SET > /dev/null real 0m7.579s # time ./実行ファイル GET > /dev/null real 0m9.030s # time ./実行ファイル DEL > /dev/null real 0m7.435s
(MEMCACHED_BEHAVIOR_NO_BLOCK,MEMCACHED_BEHAVIOR_TCP_NODELAY,MEMCACHED_BEHAVIOR_BUFFER_REQUESTS)=(ON,ON,ON) # time ./実行ファイル SET > /dev/null real 0m8.817s # time ./実行ファイル GET > /dev/null real 0m10.344s # time ./実行ファイル DEL > /dev/null real 0m8.716s
測定時間が短いので誤差も大きいでしょうが,この実験環境では,
- MEMCACHED_BEHAVIOR_NO_BLOCKはオフにした方が速い
- MEMCACHED_BEHAVIOR_TCP_NODELAYはよく分からない
- MEMCACHED_BEHAVIOR_BUFFER_REQUESTSはオンにした方が速い
という結果になりました.
ただし注意が必要で,MEMCACHED_BEHAVIOR_BUFFER_REQUESTSをオンにするとバッファリングされますので,リアルタイムにmemcachedには書き込まれません.実際,BUFFER_REQUESTSをオンにするとMEMCACHED_SUCCESSは返って来ず,上記のようなMEMCACHED_SUCCESSだけをチェックしているプログラムでは
memcached_set error=[ACTION QUEUED] for KEY[key00006] and VALUE[val00006]
と表示され,キューイングされたためにエラーになったかのような結果になってしまっています.MEMCACHED_BEHAVIOR_BUFFER_REQUESTSをオンにする場合は,MEMCACHED_SUCCESSだけでなくMEMCACHED_BUFFEREDもチェックしなければなりません.
例えば
if(rc != MEMCACHED_SUCCESS && rc != MEMCACHED_BUFFERED) { printf("memcached_get error=[%s] for KEY[%s]\n", memcached_strerror(memcached, rc),key); }
とか.
ちなみにこれは同僚が「memcachedに値を入れたはずなのに戻り値がSUCCESSになってないみたい」と気付き,libmemcachedのソースを見て知ったことです.私が無知でした.