Redis

RedisのSlaveはMasterよりも容量を大きくした方がよさそう

結論から先に書くと、
RedisのSlaveは、Masterよりも容量を大きくとった方がいい。
理由は、同一容量だと、Masterが容量をフルで使っている場合、このマスターデータ全てをSlaveにSETすることが出来ないから。

主な設定は、
maxmemory 2gb
maxmemory-policy allkeys-lru

症状
Masterがこの容量をフルに使ってる場合、新たにSlaveが同期を開始すると、途中で同期が止まる。
再現性は100%
最初は、Masterのclient-output-buffer-limitで切られていることを疑ったが違った。
おそらく全ての同期を完了する前に、Slaveの容量がいっぱいになってしまっている模様。
このレプリケーションではallkeys-lruの挙動になっていないらしく、新しめのデータがSETされていないことが多い。
ログでは正常に終了した形になっている。

Master
db0:keys=8942214,expires=8778923

Slave
master_link_status:up
db0:keys=8309380,expires=8176889

こんな形で、Slaveとして正常に動いているが、データには差異が出来てしまっている。
Slaveの容量を少しMasterよりも大きくすることで解決。

hiredis使ってみた

luaでresty.redis使ってるんですが、どうも安定しないのでhiredisを使おうと思う。
まずはhsetとhgetallのサンプル書いて動作確認してみた。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <hiredis/hiredis.h>
 
int main(void) {
 
  char       *key1 = "aaaa";
  char       *key2 = "bbbb";
  char       *val  = "1111";
  redisReply *reply;
  int         i;
 
  redisContext *c = redisConnect("127.0.0.1", 6379);
  if (c->err) {
    printf("redisConnect Error: %s\n", c->errstr);
    return (-1);
  }
 
  // HSET                                                                                                                                                                           
  reply = redisCommand(c, "HSET %s %s %s", key1, key2, val);
  freeReplyObject(reply);
 
  // HGETALL                                                                                                                                                                        
  reply = redisCommand(c, "HGETALL %s", key1);
  if ( reply->type == REDIS_REPLY_ERROR ) {
    printf("redisCommand Error: %s\n", c->errstr);
  }
  else if (reply->type == REDIS_REPLY_ARRAY) {
    printf("hgetall: %s\n", key1);
    for ( i=0; i<reply->elements; i++ ) {
      if ( (i % 2) == 0 ) {
        printf("hkey: %s, ", reply->element[i]->str);
      }
      else {
        printf("value: %s\n", reply->element[i]->str);
      }
    }
  }
  freeReplyObject(reply);
 
  return (0);
}

$ ./a.out
hgetall: aaaa
hkey: bbbb, value: 1111

RedisがDisk書き込みのタイミングで接続出来なくなる

Redisはデフォルトで定期的にDiskへバックアップをとるようになっていますが、
このタイミングでクライアントからRedisに対して接続出来なくなるようです。

僅かな差分であれば、一瞬で終るのですが、
それなりの更新があると書き込みにかかる時間も増えるのでその間、ずっと繋がらなくなります。

マニュアルには、

BGSAVE()
データベースの保存をバックグラウンドで行います。 OK コードは直ちに返って来ます。Redisはフォークし、親のプロセスはクライアントに対して処理をし続け、子のプロセスはデータベースをディスクに保存したあと死にます。クライアントから保存が無事に終わったかを LASTSAVE コマンドを使って確認することが出来ます。

とあるので、疑問だったのですが、
親プロセスが処理を「受け」続けるとは書いていないので、間違ってはいないのかな。

ということで、Disk書き込みを無効にして、スレーブの方でDisk保存するようにしました。
もし再起動などの際に、Diskに保存したい場合は、BGSAVE()を手動で発行すればOK。

Redisの処理能力の限界

sysctlで、ファイルディスクリプタやsomaxconn、ポートレンジの対応はした上でですが、
Redisの処理能力の限界がだいたい分かった。

Redisはシングルスレッドなので、マルチコアはあまり意味がないのでEC2のm1.mediumを使ってるんですが、
このインスタンスでだいたい、instantaneous_ops_per_secが12000辺りで頭打ちになる。
この時のCPU使用率が90%超えなので、CPUバウンドでしょう。

—-
この記事は、
ec2でredisを使う場合はhvmで書いたように、仮想化方式がPra-Virtualでのものなので、HVMだとかなりパフォーマンスが向上します。
c3のCPUであれば、35000~40000req/secほど出ます。