Table Of Contents

Previous topic

文字列のハック

Next topic

仮想メモリ技術仕様

This Page

プロトコル仕様

Redisのプロトコルは次のような要求を満たすものとなっています。

  • 実装しやすい
  • コンピュータにパースさせるのが楽
  • 人間がパースするのも十分に簡単

ネットワークレイヤー

Redisのクライアントは、ポート6379でサーバに接続しに行くことでコネクションを張ります。クライアント・サーバ間でやりとりされる、すべてのRedisコマンドやデータは \r\n (CRLF)で終端されます。

リクエスト

Redisは複数の異なる引き数を含むコマンドを受け取ります。コマンドを受け取ると、それを処理して、クライアント側に返信を返します。

新統一リクエスト・プロトコル

新統一リクエスト・プロトコルは、Redis 1.2から導入され、2.0からはサーバが使う標準的な方法になりました。

統一リクエスト・プロトコルの中では、すべての引き数はバイナリセーフな方法でサーバに送られます。一般的には次のような構成をしています。

*<引き数の数> CR LF
$<引き数1のバイト数> CR LF
<引き数データ> CR LF
...
$<引き数Nのバイト数> CR LF
<引き数データ> CR LF

次のようになります。

*3
$3
SET
$5
mykey
$7
myvalue

このコマンドを文字列形式で正確に表現すると、次のようになります。

"*3\r\n$3\r\nSET\r\n$5\r\nmykey\r\n$7\r\nmyvalue\r\n"

これは、Redisからのリプライにも使われます。引き数に "$6\r\nmydata\r\n" という形式を使ったものを、 バルクリプライ と呼んでいます。また、リストを返すなど、実際に統一リクエスト・プロトコルが使用されているものを、 マルチバルクリプライ と呼んでいます。これは、それぞれの引き数(バルクリプライ)の前に、引き数の数<argc>を指定するための、 *<argc>nr を前につけたものになります。

リプライ

Redisはコマンドに対して、様々な形式のリプライを返します。最初の1バイトを見ることで、どのような種類のリプライかをチェックすることができます。

  • 1行リプライの場合は、最初のバイトは + で始まります。
  • エラーメッセージの場合は、最初のバイトは - で始まります。
  • 数字の場合は、最初のバイトは : で始まります。
  • バルクリプライ の場合は、最初のバイトは $ で始まります。
  • マルチバルクリプライ の場合は、最初のバイトは * で始まります。

1行リプライ

1行リプライは1行の文字列で構成されたリプライで、 + で始まり、 \r\n で終わります。

+OK

クライアントライブラリは + の後ろの結果を返すべきです。この場合は、 "OK" になります。

1行リプライを返すコマンド: PING, SET, SELECT, SAVE, BGSAVE, SHUTDOWN, RENAME, LPUSH, RPUSH, LSET, LTRIM

エラーリプライ

エラーリプライは、1行リプライと非常に良く似ていますが、 + の代わりに - が最初のバイトとして返されます。

エラーリプライは、異なるデータ型に対する操作を実行しようとした、存在しないコマンドを実行した場合など、何か想定外のことが発生した場合に送信されます。クライアントライブラリではエラーリプライを受け取ったら、その言語の例外処理機構を使って例外を投げるべきでしょう。

数値リプライ

先頭の1バイトが : で始まり、CRLFで終端されている文字列の場合、このリプライは数値であるという意味になります。例えば、 ":0\r\n" や、 ":1000\r\n" などが数値リプライになります。

INCRLASTSAVE のようなコマンドは実際に意味のある数値を数値リプライを使って返します。 INCR の場合はインクリメンタルした数値を、 LASTSAVE は、UNIX時間を返します。

また、 EXISTS などのコマンドは、True/Falseの意味で、1や0の数値を返します。

SADDSREMSETNX などの他のコマンドは、操作が実際に行われた場合に1を、そうでない場合に0を返します。

数値リプライを返すコマンド: SETNX, DEL, EXISTS, INCR, INCRBY, DECR, DECRBY, DBSIZE, LASTSAVE, RENAMENX, MOVE, LLEN, SADD, SREM, SISMEMBER, SCARD

バルクリプライ

バルクリプライは、単一のバイナリセーフな文字列を返すために使用されます。

C: GET mykey
S: $6
S: foobar

サーバは最初に $ + バイト数 + CRLFというデータを送信し、その後実際のデータを送付し、最後に追加でCRLFの2バイトを送信します。サーバから送られる、正確な文字列は次のようになります。

"$6\r\nfoobar\r\n"

もし、要求された値が存在しない場合には、データ超として、特別に-1が返されます。

C: GET nonexistingkey
S: $-1

要求されたオブジェクトが存在しない場合には、クライアントライブラリは空の文字列ではなく、nilオブジェクトを返すようにしてください。例えば、RubyのライブラリはCのライブラリが NULL を返すべきところでは、 nil を返したり、リプライオブジェクトに特別なフラグを設定したりします。

マルチバルクリプライ

LRANGE のようなコマンドは複数の値(リストの要素もすべて値で、このコマンドは一つ以上の要素を返す)を返します。この時にはまずいくつの要素があるかを出力し、その後複数回のバルク書き出しが行われます。

C: LRANGE mylist 0 3
S: *4
S: $3
S: foo
S: $3
S: bar
S: $5
S: Hello
S: $5
S: World

みていただいた通り、マルチバルクリプライは、Redisサーバが使用する、統一プロトコルでコマンドを送るのに使用しているのとまったく同じフォーマットになっています。

サーバが送信している最初の行の "4\r\n" は、この後4回バルクリプライが送信されることを表しています。その後、回数分のバルクリプライが送信されます。

もし指定されたキーが存在しなかった場合には、リストの要素数ではなく、 -1 を返します。

C: LRANGE nokey 0 1
S: *-1

クライアントライブラリのAPIは、この場合は空のリストを返すのではなく、nilオブジェクトを返すようにしてください。これによって、例えば BLPOP コマンドのタイムアウトのように、本当に空のリストだった場合と、その他の状況を区別できるようになります。

マルチバルクリプライ中のnil要素

マルチバルクリプライの要素の長さとして -1 が返されると、その要素は空の文字列ではないというサインになります。これは、特定のキーが見つからない GET パターンを SORT の中で指定しまったときに発生する可能性があります。

S: *3
S: $3
S: foo
S: $-1
S: $3
S: bar

このリプライでは、2番目の要素がnulです。クライアントライブラリは次のような返り値を返すようにすべきです。

["foo",nil,"bar"]

複数コマンドとパイプライン

クライアントは、1回のコネクションで複数のコマンドを発行することができます。一回の書き込み操作で、複数のコマンドを送信する、 パイプライニング がサポートされています。この場合は、次のコマンドの送信をする前に、サーバからの返信を待つ必要はなくなり、終了時にすべてのリプライを受け取ります。

通常は、Redisサーバとクライアントは非常に高速にリンクを貼ることができるため、クライアントライブラリでこの機能をサポートすることは重要ではありませんが、クライアントから大量のコマンドを発行する必要があるのであれば、パイプラインを使用すると高速化できます。

古いコマンド送信プロトコル

統一リクエストプロトコルを使用する前は、Redisは異なるプロトコルでコマンドを送信していました。このプロトコルはまだサポートされており、telnet越しにタイプできるぐらいシンプルです。このプロトコルには2種類のコマンドがあります。

インラインコマンド
スペース区切りの文字列で引き数を渡す、シンプルなコマンドです。バイナリセーフではありません。
バルクコマンド
バルクコマンドはインラインコマンドと似ていますが、最後の引き数をバイナリセーフで扱えるように、最後の引き数だけを特別な方法で扱います。

インラインコマンド

Redisにコマンドを送る、もっとも簡単な方法が、このインラインコマンドです。次の例は、サーバ/クライアント間でインラインコマンドを使って通信しあっています。(サーバ側は S: 、クライアント側は C と書いています)

C: PING
S: +PONG

次の例は、数値を返す場合のインラインコマンドの例です。

C: EXISTS somekey
S: :0

somekey が存在していなかったため、 :0 が返ってきています。

EXISTS コマンドは一つの引き数を受け取リますが、ここではスペース区切りで一緒に記述していることに注意してください。

バルクコマンド

いくつかのコマンドは、最後の引き数をバイナリセーフでサーバに送信するために、特別な形式を使ってコマンドを送る必要があります。このコマンドは最後の引き数にバイト数のカウントを設定し、その後、バルクデータを送信します。サーバ側は、何バイト読み込めばよいかを知っているため、安全にこのデータを利用できます。

次の例がサンプルになります。

C: SET mykey 6
C: foobar
S: +OK

コマンドの最後の引き数が '6' になっています。これは次に続くデータのバイト数を表しています。ここでは、次に来る "foobar" という文字列です。この文字列の後ろには CRLF の2バイトが付加されます。

すべてのバルクコマンドはこの形式になっています。最後の引き数として数値が入り、この後ろに送信したいバイナリが付加され、最後にCRLFが来ます。プログラマ向けにもっとわかりやすく表現すると、上記の例は次のような文字列としてクライアントから送信されます。

"SET mykey 6\r\nfoobar\r\n"

Redisは内部で、どのコマンドがインラインで、どのコマンドがバルクなのか、というリストを持っています。クライアントはそれに従ってコマンドを送信する必要があります。現在では、このプロトコルの代わりに、新しい統一リクエストプロトコルを使うことを推奨します。