ここから少し、楽天モバイルの宣伝になります。

このサイトでアフィリエートや広告を貼るつもりは全然無かったのですが、
6月中に楽天モバイルの契約30件を取るか、船を降りるかするように言われています。

回線の増設を考えている方、お子様に新しく携帯を持たせようと考えている方、
下記リンク先で楽天にログイン後、楽天モバイルの各プランをご検討いただけないでしょうか。
楽天モバイル 紹介リンク

SCAN cursor [MATCH pattern] [COUNT count]

SCAN と、密接に関連するコマンド SSCAN, HSCAN および ZSCANは、要素のコレクション上で段階的に繰り返しを行うために使われます。

  • SCAN 現在選択されているRedisデータベース内のキーのセットを繰り返す。
  • SSCAN Set型の要素を繰り返す。
  • HSCAN ハッシュ型のフィールドとそれらに関連する値を繰り返す。
  • ZSCAN Sorted Set形の要素とそれらに関連するスコアを繰り返す。

これらのコマンドは呼び出しごとに少数の要素しか返さない逐次的繰り返しを可能にするので、呼び出された時に長時間(数秒でも)サーバをブロックする可能性があるKEYS あるいは SMEMBERSのようなコマンドの欠点無しに実働環境で使用することができます。

しかしSMEMBERS のようなブロック コマンドは与えられた瞬間にSetの一部である全ての要素を提供することができますが、繰り返し処理の中で逐次的に繰り返すコレクションは変わるかもしれないので、SCANファミリーのコマンドは返される要素について制限された保証のみを提供します。

SCAN, SSCAN, HSCAN および ZSCAN は全て非常によく似た働きをするので、このドキュメントは4つのコマンドすべてを網羅しています。しかし、SSCAN, HSCAN および ZSCAN の場合は最初の引数がSet, Hash あるいは Sorted Setの値を保持しているキー名であるという明白な違いがあります。SCAN コマンドは現在のデータベース内のキーを繰り返すため、キー名の引数を必要としません。従って繰り返されるオブジェクトはデータベース自身です。

*SCANの基本的な使い方

SCAN はカーソルベースの繰り返しです。コマンドの呼び出しの度にサーバはユーザが次の呼び出しでカーソル引数として使う必要がある更新されたカーソルを返すことを意味します。

カーソルが0に設定されると繰り返しが開始され、サーバによって返されるカーソルが0になると終了します。以下はSCANの繰り返しの例です:

redis 127.0.0.1:6379> scan 0
1) "17"
2)  1) "key:12"
    2) "key:8"
    3) "key:4"
    4) "key:14"
    5) "key:16"
    6) "key:17"
    7) "key:15"
    8) "key:10"
    9) "key:3"
   10) "key:7"
   11) "key:1"
redis 127.0.0.1:6379> scan 17
1) "0"
2) 1) "key:5"
   2) "key:18"
   3) "key:0"
   4) "key:2"
   5) "key:19"
   6) "key:13"
   7) "key:6"
   8) "key:9"
   9) "key:11"

上の例では、最初の呼び出しはカーソルとして0を使い、繰り返しを開始します。2回目の呼び出しは以前の呼び出しによって返されたカーソルを応答の最初の要素、つまり17として使います。

ご覧の通り、SCANの返り値は2つの値の配列です: 最初の値は次の呼び出しで使う新しいカーソル、2つ目の値は要素の配列です。

2回目の呼び出しで返されるカーソルが0のため、サーバは呼び出し元に繰り返しが終了し、コレクションが完全に探索されたことを通知します。カーソル値0で繰り返しを開始し、返されたカーソルが再び0になるまでSCANを呼び出すことを完全繰り返しと呼びます。

*Scanの保証

SCAN コマンドとSCANファミリーの他のコマンドは完全繰り返しに関する一連の保証をユーザに提供することができます。

  • 完全な繰り返しは、完全繰り返しの開始から終了までコレクション内に存在していた全ての要素を常に取り出します。繰り返しが開始されたときに指定された要素がコレクションの中にあり、繰り返しが終了した時にまだある場合には、SCANはどこかの時点でそれをユーザに返したことを意味します。
  • 完全な繰り返しは、完全繰り返しの開始から終了までコレクション内に存在しなかった要素を返しません。そのため繰り返しの開始前に要素が削除され、繰り返しが継続している間にその要素が追加されなかった場合は、SCANはこの要素が返されないことを保証します。

ただし、SCAN には関連付けられた状態がほとんどないため(カーソルのみ)、以下のような欠点があります:

  • 与えられた要素は複数回返されるかもしれません。重複した要素の場合を処理するのはアプリケーション次第です。例えば、複数回再適用した場合に安全な操作を実行するために返された要素のみを使用するなどです。
  • 完全な繰り返しの間にコレクションに存在していなかった要素は、返さ絵る場合と返されない場合があります: それは未定義です。

*SCAN呼び出しごとに返される要素数

SCAN ファミリー関数は呼び出しごとに返される要素の数が範囲内にあることを保証しません。コマンドは0の要素を返すことを許可されており、クライアントは返されるカーソルが0で無い限り、繰り返しが完了したと見なすべきではありません。

ただし返される要素の数は妥当です。つまり、実用的にはSCANは大規模なコレクションを繰り返す時に最大で数十の要素を整列して返すことも、あるいは繰り返されたコレクションがエンコードされたデータ構造として内部的に表現されるのに十分に小さい場合(これは小さなセット、ハッシュおよびsorted set で起こります)に1つの呼び出しでコレクションの全ての要素を返すこともできます。

ただしCOUNT オプションを使って呼び出しごとに返される要素の桁数を調整する方法があります。

*COUNT オプション

SCANは各繰り返しで返される要素の数について保証を提供しませんが、COUNT オプションを使ってSCAN の挙動を経験的に調整することができます。基本的にCOUNTを使ってユーザはコレクションから要素を取り出すための各呼び出しで行われるべき作業量をしていしました。これは実装のための単なるヒントですが、一般的に言って実装からほとんどの場合に期待できることです。

  • デフォルトのCOUNT値は10です。
  • キー空間、あるいはハッシュテーブルで表現するのに十分な大きさのSet, Hash あるいは Sorted Set を繰り返す場合、MATCH オプションが使われていないと仮定すると、サーバは通常呼び出しごとにcount またはcount 以上の要素を返します。この文章の後半でなぜ SCAN が全ての要素を同時に返すのか の章を確認してください。
  • intsets (整数のみで構成される小さなセット)としてエンコードされたSet、あるいはziplists (小さな個々の値からなる小さなハッシュとセット) としてエンコードされたハッシュおよびSorted Set を繰り返す場合、通常はCOUNTの値に関係なくSCANの最初の呼び出しで全ての要素が返されます。

重要: 繰り返しごとに同じCOUNT値を使う必要はありません。次の呼び出しで渡されるカーソルが前の呼び出しでコマンドに取得されたものである限り、呼び出し元は自由にカウントを一方の繰り返しから他方の繰り返しに変更することができます。

*MATCH オプション

パターンを唯一の引数として取るKEYS コマンドの挙動と同様に、指定された glob形式のパターンに一致する要素をのみを繰り返すことができます。

そうするには、SCANコマンドの最後にMATCH <pattern>引数を追加するだけです (全てのSCANファミリーのコマンドで機能します)。

これはMATCHを使った繰り返しの例です:

redis 127.0.0.1:6379> sadd myset 1 2 3 foo foobar feelsgood
(integer) 6
redis 127.0.0.1:6379> sscan myset 0 match f*
1) "0"
2) 1) "foo"
   2) "feelsgood"
   3) "foobar"
redis 127.0.0.1:6379>

MATCH フィルタは、データがクライアントに返される直前に、コレクションから要素が取得された後に適用されることに注意することが重要です。これは、パターンがコレクション内の極僅かな要素にしか一致しない場合、SCANがほとんどの繰り返しで要素を返さない可能性が高いことを意味します。例を以下に示します:

redis 127.0.0.1:6379> scan 0 MATCH *11*
1) "288"
2) 1) "key:911"
redis 127.0.0.1:6379> scan 288 MATCH *11*
1) "224"
2) (empty list or set)
redis 127.0.0.1:6379> scan 224 MATCH *11*
1) "80"
2) (empty list or set)
redis 127.0.0.1:6379> scan 80 MATCH *11*
1) "176"
2) (empty list or set)
redis 127.0.0.1:6379> scan 176 MATCH *11* COUNT 1000
1) "0"
2)  1) "key:611"
    2) "key:711"
    3) "key:118"
    4) "key:117"
    5) "key:311"
    6) "key:112"
    7) "key:111"
    8) "key:110"
    9) "key:113"
   10) "key:211"
   11) "key:411"
   12) "key:115"
   13) "key:116"
   14) "key:114"
   15) "key:119"
   16) "key:811"
   17) "key:511"
   18) "key:11"
redis 127.0.0.1:6379>

ご覧の通り、ほとんどの呼び出しは0個の要素を返されますが、COUNT 1000 を使用した最後の呼び出しでは、その繰り返しに対してより多くのスキャンを実行するように強制されました。

*複数の並列繰り返し

繰り返しの完全な状態は呼び出しごとにクライアントから取得され返されるカーソル内にあるため、無数のクライアントが同時に同じコレクションを繰り返すことができます。サーバ側は全く状態を取りません。

*途中で繰り返しを中止

サーバ側に状態は無いが、完全な状態はカーソルによって補足されるため、呼び出し元はどのような形でもサーバ側に通知すること無く途中で繰り返しを自由に中止することができます。無数の繰り返しを開始することができ、中止しないことが問題無くできます。

*破損したカーソルを使ったSCANの呼び出し

破損、負、範囲外、あるいはその他の無効なカーソルで SCANを呼び出すと、未定義な挙動になりますが、クラッシュすることはありません。未定義となるのは、返された要素に関する保証がSCANの実装によってもう保証できないことです。

使用できる唯一の有効なカーソルは以下の通りです:

  • 繰り返しの開始時の 0の値のカーソル値。
  • 繰り返しを続けるために、前のSCAN呼び出しによって返されたカーソル。

*終了の保証

SCANアルゴリズムは、繰り返しのコレクションのサイズが指定された最大サイズに制限されたままの場合のみ終了することが保証されます。そうでない場合、常に大きくなるコレクションを繰り返すと完全な繰り返しを終了することができないSCANになります。

これは直感的に分かり易いです: コレクションが大きくなると全ての可能な要素にアクセスするための作業が増え、繰り返しを終了する機能は SCANの呼び出し数と、コレクションが増える速度と比較されるCOUNTオプションの値に依存します。

*SCAN が1回の呼び出しで集約データ型の全ての項目を返すことがあるのはなぜか?

In the COUNT option documentation, we state that sometimes this family of commands may return all the elements of a Set, Hash or Sorted Set at once in a single call, regardless of the COUNT option value. The reason why this happens is that the cursor-based iterator can be implemented, and is useful, only when the aggregate data type that we are scanning is represented as an hash table. However Redis uses a memory optimization where small aggregate data types, until they reach a given amount of items or a given max size of single elements, are represented using a compact single-allocation packed encoding. When this is the case, SCAN has no meaningful cursor to return, and must iterate the whole data structure at once, so the only sane behavior it has is to return everything in a call.

However once the data structures are bigger and are promoted to use real hash tables, the SCAN family of commands will resort to the normal behavior. Note that since this special behavior of returning all the elements is true only for small aggregates, it has no effects on the command complexity or latency. However the exact limits to get converted into real hash tables are user configurable, so the maximum number of elements you can see returned in a single call depends on how big an aggregate data type could be and still use the packed representation.

Also note that this behavior is specific of SSCAN, HSCAN and ZSCAN. SCAN itself never shows this behavior because the key space is always represented by hash tables.

*返り値

SCAN, SSCAN, HSCAN および ZSCAN は2つの要素のマルチ一括応答を返します。最初の要素は符号無し64ビット数(カーソル)を表す文字列、2番目の要素は要素の配列をマルチ一括応答です。

  • 要素のSCAN 配列はキーのリストです。
  • 要素のSSCAN 配列はSetメンバーのリストです。
  • 要素のHSCAN 配列は、ハッシュの返された全ての要素について2つの要素、フィールドおよび値を含みます。
  • 要素のZSCAN 配列は、sorted set の返された全ての要素について2つの要素、メンバーとそれに関連付けられたスコアを含みます。

*追加の例

ハッシュ値の繰り返し。

redis 127.0.0.1:6379> hmset hash name Jack age 33
OK
redis 127.0.0.1:6379> hscan hash 0
1) "0"
2) 1) "name"
   2) "Jack"
   3) "age"
   4) "33"
TOP
inserted by FC2 system