ホームページ > データベース > Redis > Redisハッシュの実装方法

Redisハッシュの実装方法

步履不停
リリース: 2019-06-24 11:14:53
オリジナル
2955 人が閲覧しました

Redisハッシュの実装方法

0. はじめに

redis は KV タイプのインメモリ データベースです。データベース ストレージの核となるのはハッシュ テーブルです。select コマンドを実行して、 storage db, all 操作はすべてハッシュ テーブルに基づいています. ハッシュ データ構造と redis の実装については以下で分析します.

1.hash データ構造

/*Hash表一个节点包含Key,Value数据对 */
typedef struct dictEntry {
    void *key;
    union {
        void *val;
        uint64_t u64;
        int64_t s64;
        double d;
    } v;
    struct dictEntry *next; /* 指向下一个节点, 链接表的方式解决Hash冲突 */
} dictEntry;

/* 存储不同数据类型对应不同操作的回调函数 */
typedef struct dictType {
    unsigned int (*hashFunction)(const void *key);
    void *(*keyDup)(void *privdata, const void *key);
    void *(*valDup)(void *privdata, const void *obj);
    int (*keyCompare)(void *privdata, const void *key1, const void *key2);
    void (*keyDestructor)(void *privdata, void *key);
    void (*valDestructor)(void *privdata, void *obj);
} dictType;

typedef struct dictht {
    dictEntry **table; /* dictEntry*数组,Hash表 */
    unsigned long size; /* Hash表总大小 */
    unsigned long sizemask; /* 计算在table中索引的掩码, 值是size-1 */
    unsigned long used; /* Hash表已使用的大小 */
} dictht;

typedef struct dict {
    dictType *type;
    void *privdata;
    dictht ht[2]; /* 两个hash表,rehash时使用*/
    long rehashidx; /* rehash的索引, -1表示没有进行rehash */
    int iterators; /*  */
} dict;
ログイン後にコピー

2.hash データ構造図

Redisハッシュの実装方法

3. プログレッシブハッシュの説明

# dictのht[2]には2つのハッシュテーブルがあり、初めてデータを格納するときは、 ht[0] 最小サイズ 4 のハッシュ テーブルが作成されます ht[0] の size と used が一致すると、ht[1] の dict に size*2 のハッシュ テーブルが作成されます、ht[は直接使用されません。0] のデータが ht[0] にコピーされ、プログレッシブ リハッシュが実行されます。つまり、後続の操作 (検索、設定、取得など) でゆっくりとコピーされます。新しく追加された要素は将来 ht[.0] に追加されるため、ht[1] がいっぱいになると、ht[0] 内のすべてのデータが確実に ht[1] にコピーされます。 ##4. ハッシュ テーブルを作成する

ハッシュ テーブルを作成するプロセスは非常に簡単で、dictCreate 関数を呼び出し、メモリを割り当て、中間変数を初期化するだけです。 . 要素の追加

ハッシュ テーブルに要素を追加するには、まずスペースが十分であるかどうかを判断し、次にキーに対応するハッシュ値を計算し、追加する必要があるキーと値をテーブルに追加します。 .

dict *dictCreate(dictType *type, void *privDataPtr)
{
     /*分配内存*/
    dict *d = zmalloc(sizeof(*d));
     /*初始化操作*/
    _dictInit(d,type,privDataPtr);
    return d;
}
ログイン後にコピー

6. 要素の検索

要素の検索手順は、まずハッシュ値を計算し、ht[0]とht[1]のインデックス位置を計算して検索します。 ##

int dictAdd(dict *d, void *key, void *val)
{
     /*添加入hash表中, 返回新添加元素的实体结构体*/
    dictEntry *entry = dictAddRaw(d,key);

    if (!entry) return DICT_ERR;
     /*元素val值放入元素实体结构中*/
    dictSetVal(d, entry, val);
    return DICT_OK;
}
/*
*添加元素实体函数
*/
dictEntry *dictAddRaw(dict *d, void *key)
{
    int index;
    dictEntry *entry;
    dictht *ht;

    if (dictIsRehashing(d)) _dictRehashStep(d);

    /*根据key值计算新元素在hash表中的索引, 返回-1则表示元素已存在, 直接返回NULL*/
    if ((index = _dictKeyIndex(d, key)) == -1)
        return NULL;

    /*如果在进行rehash过程,则新元素添加到ht[1]中, 否则添加到ht[0]中 */
    ht = dictIsRehashing(d) ? &d->ht[1] : &d->ht[0];
    entry = zmalloc(sizeof(*entry));
    entry->next = ht->table[index];
    ht->table[index] = entry;
    ht->used++;

    /*设置元素key*/
    dictSetKey(d, entry, key);
    return entry;
}
/*
*计算索引的函数
*/
static int _dictKeyIndex(dict *d, const void *key)
{
    unsigned int h, idx, table;
    dictEntry *he;

    /* 判断hash表是否空间足够, 不足则需要扩展 */
    if (_dictExpandIfNeeded(d) == DICT_ERR)
        return -1;
         
    /* 计算key对应的hash值 */
    h = dictHashKey(d, key);
    for (table = 0; table <= 1; table++) {
          /*计算索引*/
        idx = h & d->ht[table].sizemask;
        /*遍历冲突列表, 判断需要查找的key是否已经在冲突列表中*/
        he = d->ht[table].table[idx];
        while(he) {
            if (dictCompareKeys(d, key, he->key))
                return -1;
            he = he->next;
        }
        if (!dictIsRehashing(d)) break;
    }
    return idx;
}
/*
*判断hash表是否需要扩展空间
*/
static int _dictExpandIfNeeded(dict *d)
{
    /*redis的rehash采用的渐进式hash, rehash时分配了原来两倍的内存空间, 在rehash阶段空间必定够用*/
    if (dictIsRehashing(d)) return DICT_OK;

    /* hash表是空的需要初始化空间, 默认是4*/
    if (d->ht[0].size == 0) return dictExpand(d, DICT_HT_INITIAL_SIZE);

    /* 已使用空间满足不了设置的条件*/
    if (d->ht[0].used >= d->ht[0].size &&
        (dict_can_resize ||
         d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
    {
          /*扩展空间, 使用空间的两倍*/
        return dictExpand(d, d->ht[0].used*2);
    }
    return DICT_OK;
}

/*
*扩展空间或者初始化hash表空间
*/
int dictExpand(dict *d, unsigned long size)
{
    dictht n;
     /* 对需要分配大小圆整为2的倍数 */
    unsigned long realsize = _dictNextPower(size);

    /* 如果空间足够则表明调用错误 */
    if (dictIsRehashing(d) || d->ht[0].used > size)
        return DICT_ERR;

    n.size = realsize;
    n.sizemask = realsize-1;
    n.table = zcalloc(realsize*sizeof(dictEntry*));
    n.used = 0;
    
     /*hash表为空初始化hash表*/
    if (d->ht[0].table == NULL) {
        d->ht[0] = n;
        return DICT_OK;
    }

    /*新分配的空间放入ht[1], 后面一步一步进行rehash*/
    d->ht[1] = n;
    d->rehashidx = 0;
    return DICT_OK;
}
ログイン後にコピー

7. 要素の削除

要素を削除するには、まず要素を検索し、次にハッシュ テーブルから要素を削除します。これで、dictDelete を呼び出して要素を削除すると、スペースも削除されます要素

dictEntry *dictFind(dict *d, const void *key)
{
    dictEntry *he;
    unsigned int h, idx, table;

    if (d->ht[0].size == 0) return NULL;
    
     /*如果正在进行rehash, 执行一次rehash*/
    if (dictIsRehashing(d)) _dictRehashStep(d);
    
    h = dictHashKey(d, key);
    
     /*由于可能正在rehash, 因此要从ht[0]和ht[1]中分别进行查找, 找不到返回NULL*/
    for (table = 0; table <= 1; table++) {
        idx = h & d->ht[table].sizemask;
        he = d->ht[table].table[idx];
          /*遍历冲突列表查找元素*/
        while(he) {
            if (dictCompareKeys(d, key, he->key))
                return he;
            he = he->next;
        }
        if (!dictIsRehashing(d)) return NULL;
    }
    return NULL;
}
ログイン後にコピー
ハッシュ コマンド

ハッシュ コマンドの操作は比較的単純です。デフォルトのストレージ構造を表すハッシュを作成する場合、それは辞書ではないことに注意してください。しかし、ziplist 構造です。

redis の Ziplist データ構造を参照できます。hash_max_ziplist_entries と hash_max_ziplist_value の値は、しきい値として使用されます。hash_max_ziplist_entries は、ziplist 内の要素の数がこの値を超えると、この値を超える必要があることを意味します。 dict 構造に変換; hash_max_ziplist_value ziplist 内のデータ長がこの値を超えると、dict 構造に変換する必要があることを示します。

#Redis 関連の技術記事の詳細については、

Redis チュートリアル

## 列にアクセスして学習してください。

以上がRedisハッシュの実装方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート