Redis Scanの使い方及びSpring redisのピット


SpringRedisTemplateは、このScanをカプセル化し、使用例を示します(最新ライブラリspring-data-redis-1.8.1.1.RELEASE):
Set execute = redisTemplate.execute(new RedisCallback<Set>() {

    @Override
    public Set doInRedis(RedisConnection connection) throws DataAccessException {

        Set binaryKeys = new HashSet<>();

        Cursor cursor = connection.scan( new ScanOptions.ScanOptionsBuilder().match("test*").count(1000).build());
        while (cursor.hasNext()) {
            binaryKeys.add(new String(cursor.next()));
        }
        return binaryKeys;
    }
});

注意Cursorは必ず閉じることができません.以前のバージョンでは、ここでCursorは手動で閉じる必要がありましたが、1.8からです.0開始、手動で閉じることはできません!異常を報告します.
ScanOptionsには2つのパラメータがあり、1つはmatchで、もう1つはcountで、それぞれscanコマンドの2つのパラメータに対応しています.
Scanコマンドソース:
 /* Handle the case of a hash table. */
    ht = NULL;
    if (o == NULL) {//   
        ht = c->db->dict;
    } else if (o->type == REDIS_SET && o->encoding == REDIS_ENCODING_HT) {
        ht = o->ptr;
    } else if (o->type == REDIS_HASH && o->encoding == REDIS_ENCODING_HT) {
        ht = o->ptr;
        count *= 2; /* We return key / value for this type. */
    } else if (o->type == REDIS_ZSET && o->encoding == REDIS_ENCODING_SKIPLIST) {
        zset *zs = o->ptr;
        ht = zs->dict;
        count *= 2; /* We return key / value for this type. */
    }
//  redis ziplist, intset        ,         。   else if      。      key 。
    if (ht) {//     ,  intset, ziplist
        void *privdata[2];

        /* We pass two pointers to the callback: the list to which it will
         * add new elements, and the object containing the dictionary so that
         * it is possible to fetch more data in a type-dependent way. */
        privdata[0] = keys;
        privdata[1] = o;
        do {
            //     , cursor  ,              keys       。
            cursor = dictScan(ht, cursor, scanCallback, privdata);
        } while (cursor && listLength(keys) < count);     } else if (o->type == REDIS_SET) {
        int pos = 0;
        int64_t ll;

        while(intsetGet(o->ptr,pos++,&ll))//   set         ,       intset,    。
            listAddNodeTail(keys,createStringObjectFromLongLong(ll));
        cursor = 0;
    } else if (o->type == REDIS_HASH || o->type == REDIS_ZSET) {//     ziplist ,          ,    。
        unsigned char *p = ziplistIndex(o->ptr,0);
        unsigned char *vstr;
        unsigned int vlen;
        long long vll;

        while(p) {//     ,         。    cursor 0      。           
            ziplistGet(p,&vstr,&vlen,&vll);
            listAddNodeTail(keys,
                 (vstr != NULL) ? createStringObject((char*)vstr,vlen) : createStringObjectFromLongLong(vll));
            p = ziplistNext(o->ptr,p);
        }
        cursor = 0;
    } else {
        redisPanic("Not handled encoding in SCAN.");
    }

RedisのSCANオペレーションは、全体的なデータ設計のため、特に正確なscanオペレーションを提供することができず、「can't guarantee,just domy best」の実装にすぎないことがわかります.
  • はキー空間の遍歴操作を提供し、カーソル、複雑度O(1)をサポートし、全体的に遍歴するにはO(N)しか必要ない.
  • は、結果パターンマッチングを提供する.
  • は、一度に返されるデータバー数の設定をサポートしていますが、hintsだけで、返されることが多い場合があります.
  • 弱い状態で、すべての状態はクライアントがカーソルを維持する必要があるだけです.
  • は完全なスナップショット遍歴を提供できません.つまり、データの変更があれば、変更に関連するデータ遍歴ができない場合があります.
  • が返すたびに返されるデータの数は一定ではなく、内部に極度に依存して実現される.
  • が返すデータは重複する可能性があり、アプリケーション層は再入力ロジックを処理することができる必要がある.上記のサンプルコードでは、redisTemplate.executeメソッドはSetであり、返されたkeyの重量除去
  • に相当する.
  • countは、スキャンごとのkey個数であり、結果セット個数ではない.countはスキャンデータ量の大きさによって異なり、Scanはロックされていないが、百万以上のデータ量レベルでの検索効率を保証することはできない.countは小さすぎてはいけません.ネットワークのインタラクションが多くなります.countはできるだけ大きくしなければなりません.検索結果セット1万以内では、収集するサイズと同じ
  • に直接設定することを推奨する.