KVS最速テスト第二弾(memcached V.S. Redis)
前回のテストで,とりあえずキャッシュとしての速度だけ考えるとmemchachedがやはり最速だということが分かったので,今回はmemcachedと話題のRedisを比較しました.Redisは非同期でディスクに書き出されるのが特徴ですが,memcachedはともかく,memcached-haはレプリケーションで他サーバに飛ばせるので,それほど負けてもいません.
RedisもKyoto Tycoonと同様,memcachedプロトコルじゃないので,ソケット通信をPocoで実装しました.
#include <sstream> #include <iostream> #include <Poco/Net/HTTPClientSession.h> #include <Poco/Net/StreamSocket.h> #include <Poco/Net/SocketAddress.h> #include <Poco/Net/HTTPRequest.h> #include <Poco/Net/HTTPResponse.h> #include "Poco/StreamCopier.h" using namespace std; #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #include <sys/types.h> #include <md5.h> #include <omp.h> void set_key_val(MD5_CTX *con, int seed, char *key, char *val) { sprintf(key,"stress-test-%010d",seed); sprintf(val,"%010d",seed); MD5Init(con); MD5Update(con, key, strlen(key)); MD5End(con, key); } #define MAX 1000000 #define BUFFER_SIZE 256 int main(int argc, char *argv[]) { int suc = 0, i; time_t start, set_end, get_end; int n = omp_get_max_threads(); vector<Poco::Net::StreamSocket *> pool(n); Poco::Net::SocketAddress server(argv[1], 11211); for(vector<Poco::Net::StreamSocket *>::iterator i=pool.begin(); i!=pool.end();++i) { *i = new Poco::Net::StreamSocket(server); // スレッド数だけコネクションプールを作る } start = time(NULL); #pragma omp parallel for shared(suc,i) for(i=0;i<MAX;++i) { Poco::Net::StreamSocket *client=pool.at((int)(i/(MAX/n))); MD5_CTX con; char key[33], val[11]; set_key_val(&con, i, key, val); char write_buffer[BUFFER_SIZE], read_buffer[BUFFER_SIZE]; int sent_len, recv_len; snprintf(write_buffer, BUFFER_SIZE, "*3\r\n$3\r\nSET\r\n$%d\r\n%s\r\n$%d\r\n%s\r\n", strlen(key), key, strlen(val), val); sent_len = client->sendBytes((void *)write_buffer, strlen(write_buffer)); // sent contents //cout<<write_buffer<<endl; recv_len = client->receiveBytes(read_buffer, BUFFER_SIZE); // received contents //cout<<read_buffer<<endl; if (sent_len != strlen(write_buffer) || strncmp(read_buffer,"+OK\r\n", 5)!=0) { fprintf(stderr, "set: %s: redis error %s", key, read_buffer); fprintf(stderr, "\n"); }else { ++suc; } client = NULL; } set_end = time(NULL); #pragma omp parallel for for(i=0;i<MAX;++i) { Poco::Net::StreamSocket *client=pool.at((int)(i/(MAX/n))); MD5_CTX con; char key[33], val[11]; set_key_val(&con, i, key, val); char write_buffer[BUFFER_SIZE], read_buffer[BUFFER_SIZE]; char confirm_buffer[BUFFER_SIZE]; int sent_len, recv_len; snprintf(write_buffer, BUFFER_SIZE, "*2\r\n$3\r\nGET\r\n$%d\r\n%s\r\n", strlen(key), key); sent_len = client->sendBytes((void *)write_buffer, strlen(write_buffer)); // sent contents //cout<<write_buffer<<endl; recv_len = client->receiveBytes(read_buffer, BUFFER_SIZE); read_buffer[recv_len] = '\0'; // received contents //cout<<read_buffer<<endl; snprintf(confirm_buffer, BUFFER_SIZE, "$%d\r\n%s\r\n", strlen(val), val); if (sent_len != strlen(write_buffer) || strncmp(read_buffer, confirm_buffer, strlen(confirm_buffer))!=0) { fprintf(stderr, "get: %s: redis error %s", key, read_buffer); fprintf(stderr, "\n"); }else { ++suc; } } get_end = time(NULL); printf("try=%d success=%d ratio=%f\n",MAX,suc/2,(double)suc/2/MAX*100.0); printf("set=%d[sec], get=%d[sec], total=%d[sec]\n", set_end-start, get_end-set_end, get_end-start); return(0); }
memcachedは前回と同じくlibmemcachedを使ったもので.
memcachedもredisも速いと予想されるので,Key-Valueの数(=ループ回数)も10倍にして検証精度を高めました.すなわち,ループ回数MAX=100万回に.
ただ,そうするとmemcachedの方がデフォルトの記憶容量64Mbyteを超えて最初の方にsetした値が消えてしまったので,確保容量を拡張するように立ち上げました.
./memcached-ha -p 11211 -m 256
Redisはデフォルト1Gっぽいです.
server.vm_max_memory = 1024LL*1024*1024*1; /* 1 GB of RAM */
今回もOpenMPを使ってスレッド数を変化させて同時並行アクセス数を変えてテストしました.
とりあえず,やはりmemcached-haが最速でした.
速度だけ見ると,memcachedの圧勝です.
ただし,memcahcedがひたすらシンプルなのに対し,RedisやKyoto Tycoonは性能が良いのは確かです.
どれにも言えることは,プロトコルが分かりやすいことです.SQL/RDBMSに比べると,格段に拡張性や利便性(ボトルネックの調査のしやすさなど)が優れています.