RedisにおけるZSETのZCARD動作の時間的複雑さ
2628 ワード
最近ではRedisのZSET構造が用いられるが,あるRedisコマンド紹介サイトにZSETと表記されているZCARDコマンドの複雑さはO(1)である.しかし、私のコードの中でZCARDは頻繁に操作されています.念のため、Redisのソースコードをダウンロードして、ZCARDの操作に関するコードを見つけました.
ここでzsetオブジェクトのencodingを判断し,skiplist,すなわちホップテーブルであればlength属性を1つ直接取得すればよい,複雑度はO(1)であるがziplist圧縮チェーンテーブルで格納すると異なることが分かる.関連コードは次のとおりです.
このコードの意味は、マクロ操作によってziplistの長さが得られ、ziplistの長さがUINT 16_より小さい場合MAX、すなわち16ビット符号なし整数の最大値、65535は、直接戻り、そうでなければziplistを巡って長さを取得する.遍歴すると複雑度はO(N)です.マクロの操作は次のとおりです(理解できませんでした):
著者らの注釈では,ziplistが直接取得できる最大長は実際には2^16−2であり,この数より大きいものはZCARDで遍歴すると述べている.
しかし、RedisがZSETを処理するポリシーは構成可能である.
このコードから、新しいzsetを作成するときにzset_がmax_ziplist_entriesパラメータが0に等しいか、現在の挿入値の長さがzset-max-ziplist-valueパラメータより大きい場合、createZsetObject関数によってskiplist符号化zset(コードが貼られていない)が作成されます.そうでない場合、ziplist符号化zsetが作成されます.
したがって、confファイルでzset_を構成する限りmax_ziplist_entriesパラメータが0またはzset-max-ziplist-valueパラメータが小さい場合、ziplist構造ではなくskiplist構造が作成されることを保証し、ZCARDの複雑さがO(N)であることを保証することができます.ziplistの長さがzsetより大きい場合max_ziplist_entriesパラメータ設定の長さの場合も、自動的にskiplistに変わります.zset_max_ziplist_entriesのデフォルト値128.
unsigned long zsetLength(const robj *zobj) {
unsigned long length = 0;
if (zobj->encoding == OBJ_ENCODING_ZIPLIST) {
length = zzlLength(zobj->ptr);
} else if (zobj->encoding == OBJ_ENCODING_SKIPLIST) {
length = ((const zset*)zobj->ptr)->zsl->length;
} else {
serverPanic("Unknown sorted set encoding");
}
return length;
}
ここでzsetオブジェクトのencodingを判断し,skiplist,すなわちホップテーブルであればlength属性を1つ直接取得すればよい,複雑度はO(1)であるがziplist圧縮チェーンテーブルで格納すると異なることが分かる.関連コードは次のとおりです.
unsigned int ziplistLen(unsigned char *zl) {
unsigned int len = 0;
if (intrev16ifbe(ZIPLIST_LENGTH(zl)) < UINT16_MAX) {
len = intrev16ifbe(ZIPLIST_LENGTH(zl));
} else {
unsigned char *p = zl+ZIPLIST_HEADER_SIZE;
while (*p != ZIP_END) {
p += zipRawEntryLength(p);
len++;
}
/* Re-store length if small enough */
if (len < UINT16_MAX) ZIPLIST_LENGTH(zl) = intrev16ifbe(len);
}
return len;
}
このコードの意味は、マクロ操作によってziplistの長さが得られ、ziplistの長さがUINT 16_より小さい場合MAX、すなわち16ビット符号なし整数の最大値、65535は、直接戻り、そうでなければziplistを巡って長さを取得する.遍歴すると複雑度はO(N)です.マクロの操作は次のとおりです(理解できませんでした):
/* Return the length of a ziplist, or UINT16_MAX if the length cannot be
* determined without scanning the whole ziplist. */
#define ZIPLIST_LENGTH(zl) (*((uint16_t*)((zl)+sizeof(uint32_t)*2)))
著者らの注釈では,ziplistが直接取得できる最大長は実際には2^16−2であり,この数より大きいものはZCARDで遍歴すると述べている.
しかし、RedisがZSETを処理するポリシーは構成可能である.
if (zobj == NULL) {
if (xx) goto reply_to_client; /* No key + XX option: nothing to do. */
if (server.zset_max_ziplist_entries == 0 ||
server.zset_max_ziplist_value < sdslen(c->argv[scoreidx+1]->ptr))
{
zobj = createZsetObject();
} else {
zobj = createZsetZiplistObject();
}
dbAdd(c->db,key,zobj);
}
このコードから、新しいzsetを作成するときにzset_がmax_ziplist_entriesパラメータが0に等しいか、現在の挿入値の長さがzset-max-ziplist-valueパラメータより大きい場合、createZsetObject関数によってskiplist符号化zset(コードが貼られていない)が作成されます.そうでない場合、ziplist符号化zsetが作成されます.
したがって、confファイルでzset_を構成する限りmax_ziplist_entriesパラメータが0またはzset-max-ziplist-valueパラメータが小さい場合、ziplist構造ではなくskiplist構造が作成されることを保証し、ZCARDの複雑さがO(N)であることを保証することができます.ziplistの長さがzsetより大きい場合max_ziplist_entriesパラメータ設定の長さの場合も、自動的にskiplistに変わります.zset_max_ziplist_entriesのデフォルト値128.