Table Of Contents

Previous topic

データ型

Next topic

リスト型

This Page

文字列型

文字列型はRedisで扱う型の中で最も基本的なものです。Redis文字列型はバイナリセーフです。つまりRedis文字列型はどんな種類のデータも保持できるということです。たとえばJPEGイメージやシリアライズされたRubyオブジェクトなども持つことができます。

文字列は最大1GBまで扱うことが出来ます。

文字列型は INCR コマンド群からは整数値として扱われます。この点において、整数値は符号付き64bit値に制限されます。

Redisリスト型、Redisセット型、Redisソート済みセット型、Redisハッシュ表型で保持される各要素はRedis文字列型であることを覚えておいてください。

実装の詳細

Redis文字列型は sds.c (simple dynamic strings) という動的文字列ライブラリを用いて実装されています。このライブラリは文字列のある時点での長さをキャッシュするので、Redis文字列型の長さの取得はO(1)の操作となっています。(現在 STRLEN コマンドはないですが、そのうち実装されると思います)

Redis文字列はRedisオブジェクト内にカプセル化されています。Redisオブジェクトは参照カウントのメモり管理システムを持っているので、1つのRedisストリングは異なるデータセット内で共有されても大丈夫です。つまり、同じ文字列をたくさん使いたい場合には、Redisは同じ文字列を毎回作成してアロケートするのではなく、1つの文字列を使い回そうとします。(設定ファイル内でオブジェクトの共有の項目をオンにしている場合です)

バージョン1.1からRedisでは文字列を特別な方法でエンコードして数値のみの形にすることができるようになりました。メモリ使用量を減らすために文字列を文字の配列として保存する代わりに、Redisは整数値を保存しています。これによって、Redis 1.0と比較してメモリ使用量を30%減らすことができました。

文字列型のコマンド

SET(key, value)

計算時間: O(1)

文字列値 value をキー key にセットします。文字列は1073741824バイト(1GB)以下でなければいけません。

返り値

Status code replyを返す。
GET(key)

計算時間: O(1)

指定したキー key に対応する値を取得します。もしキーが存在しなかったら特別な値 “nil” を返します。もしキーに対応する値が文字列型ではなかったらエラーが返ります。なぜなら GET は文字列型にしか対応していないからです。

返り値

Bulk replyを返す。
GETSET(key, value)

計算時間: O(1)

GETSET はキー key に新しい値 value をセットして、そのキーにセットされていた古い値を返すアトミックなコマンドです。文字列型は1073741824バイト(1GB)以下でなければなりません。

返り値

Bulk replyが返ります。

デザインパターン

MGET(key1, key2, ..., keyN)

計算時間: キー1つにつきO(1)

指定したすべてのキー keyN にひもづいた値を取得します。もし一つ以上のキーが存在しない、または紐づいた値が文字列型でない場合 “nil” が返ってきます。操作が失敗で終了することはありません。

返り値

Multi bulk replyが返ります。

例:

$ ./redis-cli set foo 1000
+OK
$ ./redis-cli set bar 2000
+OK
$ ./redis-cli mget foo bar
1. 1000
2. 2000
$ ./redis-cli mget foo bar nokey
1. 1000
2. 2000
3. (nil)
$
SETNX(key, value)

計算時間: O(1)

SETNXSET と似たコマンドです。唯一の違いはキーが存在する場合は処理が行われない点です。 SETNX の意味は “SET if Not eXists” です。

返り値

Integer が帰って来ます。具体的には下記:

1 if the key was set
0 if the key was not set

デザインパターン: SETNXを用いてロックを実装する

SETNX はロックのためのプリミティブとみなすこともできます。たとえば foo というキーのロックを取得するには、クライアントは次のように書くことができます:

SETNX lock.foo <current UNIX time + lock timeout + 1>

SETNX はクライアントがロックを取得したら1を返し、 lock.foo というキーにUNIX時間をセットし、その時間からロックが他のクライアントから収得できない状況にします。クライアントは DEL を使って look.foo を削除しロックをリリースします。

もし SETNX が0を返した場合はロックが他のクライアントに既に収得されていることを意味します。この場合、呼び出し元にノンブロッキングロックを返すかタイムアウトするかロックを無事取得できるまでリトライを繰り返すかどちらかになるでしょう。

デッドロックを扱う

上記のロックアルゴリズムでは問題があります。クライアントが失敗したり、クラッシュしたり、とにかくロックを解放できなくなった場合どうなるでしょうか。ロックキーはUNIX時間を保持しているのでこの状況を検知することは可能です。もしタイムスタンプが現在のUNIX時間以前のものであれば、ロックを取得することはできません。

このような状況が起きた時は競合状態なので、そのキーに対して単純に DEL を呼び出すことはできません。代わりに SETNX を呼びます。

  • SETNX がC1とC2に0を返すので、C1とC2がlock.fooを読み取ってタイムスタンプを確認します。これはC3がロックを取得した後にクラッシュしてそのままになってしまったことによります。
  • C1が DEL lock.foo を呼び出します
  • C1が SETNX を呼び出します => 成功!
  • C2が DEL lock.foo を呼び出します
  • C2が SETNX を呼び出します => 成功!
  • ERROR: 競合状態だったのでC1とC2がロックを取得しました

幸いにも、このような問題は以下のようなアルゴリズムを使うことで避けられます。試しに良識あるクライアントC4が参加した場合にこのアルゴリズムを使ったとしてどうなるか、見てみましょう:

* C4がロックを取得するために ``SETNX lock.foo`` を送ります
  • クラッシュしたクライアントC3がまだロックを保持しています。なのでRedisはC4に0を返します。
  • C4は GET lock.foo を送ってロックの有効期限が切れたか確認します。もし(たとえば)取得に1秒かかったとしたら始めからやり直します。
  • もしロックlock.fooのUNIX時間が現在のUNIX時間よりも昔のもので有効期限切れになっているとわかったら、C4は GETSET lock.foo <現在のUNIX時間 + ロックのタイムアウト + 1> の呼び出しを試みます
  • GETSET コマンドのおかげで、C4には有効期限切れの値がセットされていたか確認できます。もし確認できたら、ロックを取得できたということになります!
  • あるいはもし他のクライアントC5がC4よりも早く GETSET コマンドを発行してロックを収得してしまったら、C4の GETSET の操作は有効期限切れでないタイムスタンプを返します。C4は単純に手順を最初からやり直します。ここでC4がキーに値をセットしてしまったとしても、ちょっと経てばこれは問題にならないということに注意してください。

Warning

このロック機構をよりロバストにするために、ロックを保持しているクライアントはロックを解放するために DEL を実行する前にタイムアウト時間が有効期限切れになっていないか常に確認すべきです。なぜならクライアントのクライアントの失敗は複雑になりがちで、単純にクラッシュするだけじゃなく多くの操作に対してなギア何度もブロックをかけてしまったり、さらにはそのあと何回も DEL を発行しようとしたりしてしまいます。(ロックが既にほかのクライアントに保持されているときの話です)

SETEX(key, time, value)

計算時間: O(1)

このコマンドは次の一連のコマンドと等価です:

SET _key_ _value_
EXPIRE _key_ _time_

この操作はアトミックです。アトミックな SET+EXPIRE の操作は既に MULTI/EXEC で提供されていますが、 SETEX はより速い操作となっています。なぜならこの類いの操作はRedisがキャッシュとして用いられるときに非常によく行われるからです。

返り値

Status code replyが返ります
MSET(key1, value1, key2, value2, ..., keyN, valueN)

New in version 1.1.

MSETNX(key1, value1, key2, value2, ..., keyN, valueN)

New in version 1.1.

計算時間: キーそれぞれに対してO(1)

それぞれのキーに対してそれぞれの値をセットします。 MSET は古い値を新しい値で上書きする一方で、 MSETNX はたった1つでもキーが既存であれば一切の操作は行われません。

このセマンティクスによって MSETNX は1つのユニークな論理オブジェクトの異なったフィールドを表す異なったキーに値がセットされたかもしくはどのキーにも値がセットされていないか確認しながらキーに値をセットできます。

MSETMSETNX はともにアトミックな操作です。これはつまり、たとえばキーAとキーBが修正されたらRedisに接続している他のクライアントがAまたはBに変更が起きたかあるいはまったく変更が起きなかったかを一度に確認することができます。

MSETの返り値

Status code reply が返ります。基本的に MSET は失敗しないので +OK が返ります。

MSETNXの返り値

Integer replyが返ります。具体的には:

1 if the all the keys were set
0 if no key was set (at least one key already existed)
INCR(key)
INCRBY(key, integer)
DECR(key, integer)
DECRBY(key, integer)

計算時間: O(1)

キーに対応する値を1つインクリメントまたはデクリメントします。キーが存在しない場合または間違った型の値が保持されていた場合は、インクリメントまたはデクリメントの操作をする前に、キーに対応する値を “0” にセットします。

INCRBYDECRBYINCRDECR と似た動作をしますが、1つインクリメント/デクリメントする代わりに、増減させる量は integer で指定します。

INCR コマンドは64bitの符号付き整数の範囲に制限されています。

Note

これらの操作は実際には文字列型の操作です。つまりRedisには「整数型」がないのです。単純にキーに保存された文字列はbase-10の64bit符号付き整数として読み取られ、インクリメントされて、再び文字列に変換されて戻されます。

返り値

Integer replyが返ります。インクリメントまたはデクリメントされた結果の値が返ります。
APPEND(key, value)

計算時間: O(1). 追加される値が小さいという想定をした倍のならし計算時間はO(1)です。なぜならRedisで用いられている動的文字列ライブラリはアロケートしなおす際に必要なサイズの倍を取得しておくからです。

もしキー key が既に存在して、それにひもづいた値が文字列の場合、このコマンドはキーに対応する値の文字列の末尾に value を結合します。もしキーが存在しなかった場合は value の値を持った新しい文字列が作成されます。この特別な状況だけ APPENDSET にとてもよく似ているといえます。

Note

原文ではキーが存在しない場合空の文字列ができるって書いてあるけどホント?

返り値

Integer replyが返ります。具体的には文字列の結合が行われた後の文字列長が返ります。

:

redis> exists mykey
(integer) 0
redis> append mykey "Hello "
(integer) 6
redis> append mykey "World"
(integer) 11
redis> get mykey
"Hello World"
SUBSTR(key, start, end)

計算時間: O(start+n) (startは開始インデックスでnはリクエストされた範囲の長さです)このコマンドの参照操作の部分はO(1)なので小さな文字列においてはO(1)となります。

ある文字列のオフセット start からオフセット end までのサブセットを返します。(両方のオフセットは包含的でなければいけません)負数のオフセットは文字列の末尾からのオフセットを使うために用います。なので-1は末尾の文字を表し、-2は最後から2番目の値を表す、といった具合になります。

この関数は範囲を超えた値に関してはエラーを上げることなく、文字列の範囲内で返せる値を返すという処理を行います。

返り値

Bulk replyが返ります。

:

redis> set s "This is a string"
OK
redis> substr s 0 3
"This"
redis> substr s -3 -1
"ing"
redis> substr s 0 -1
"This is a string"
redis> substr s 9 100000
" string"