Redis深度歷險-字典迭代器
在Redis中比較重要的一部分就是迭代器,因爲Redis是單線程運行的,必須爲外部提供
O(1)
級別的接口;包括集合以及哈希表都是以字典來實現的,如hscan
接口;代碼在src/dict.c
和hiredis/dict.c
結構體定義
typedef struct dictIterator {
dict *d; //所屬字典
long index; //index就是迭代器當前所在的字典數組索引
int table, safe; //當safe爲true時會暫停rehash操作,table表示當前在字典中哪個hash表
dictEntry *entry, *nextEntry; //迭代器當前指向的數據及其下一個
long long fingerprint; //一個64bit的數字用來表示字典狀態
} dictIterator;
這裏的safe
是針對rehash
來說的,會暫停rehash
;字典的漸進式rehash
是通過兩個ht
數組實現的,這裏的table
字段就是用來表示當前在哪個ht
中的fingerprint
是字典的指紋,在創建迭代器時生成、在銷燬迭代器時比較,如果發現兩者不一致則表示在迭代過程中進行了異常操作
迭代
創建迭代器
dictIterator *dictGetIterator(dict *d)
{
dictIterator *iter = zmalloc(sizeof(*iter));
iter->d = d;
iter->table = 0;
iter->index = -1;
iter->safe = 0;
iter->entry = NULL;
iter->nextEntry = NULL;
return iter;
}
dictIterator *dictGetSafeIterator(dict *d) {
dictIterator *i = dictGetIterator(d);
i->safe = 1;
return i;
}
開始迭代
dictEntry *dictNext(dictIterator *iter)
{
while (1) {
//因爲Redis中以鏈表法來解決衝突,所以一個索引下可能會有多個值
//entry就是一個鏈表,如果不爲空則順着鏈表往下
if (iter->entry == NULL) {
dictht *ht = &iter->d->ht[iter->table];
//在剛開始迭代時進行一些處理
if (iter->index == -1 && iter->table == 0) {
//如果有safe標記則暫停漸進式rehash;否則記錄字典指紋
if (iter->safe)
dictPauseRehashing(iter->d);
else
iter->fingerprint = dictFingerprint(iter->d);
}
iter->index++;
//索引是否超過hash表
if (iter->index >= (long) ht->size) {
//如果正在進行rehash操作,那麼可能ht[1]也有部分數據,將table切換到1上
if (dictIsRehashing(iter->d) && iter->table == 0) {
iter->table++;
iter->index = 0;
ht = &iter->d->ht[1];
} else {
break;
}
}
iter->entry = ht->table[iter->index];
} else {
iter->entry = iter->nextEntry;
}
if (iter->entry) {
//這裏主要是防止返回的entry被外部銷燬掉了
iter->nextEntry = iter->entry->next;
return iter->entry;
}
}
return NULL;
}
很簡單的操作就是順着字典哈希表數組往下走,主要是注意哈希表每個索引上都是一個鏈表
釋放迭代器
void dictReleaseIterator(dictIterator *iter)
{
if (!(iter->index == -1 && iter->table == 0)) {
if (iter->safe)
//重新開始rehash操作
dictResumeRehashing(iter->d);
else
//防止迭代過程中進行了異常操作
assert(iter->fingerprint == dictFingerprint(iter->d));
}
zfree(iter);
}