当前位置:   article > 正文

redis源码分析 2 迭代器_redis iterator

redis iterator

迭代器,在很多的高级语言都会有该实现,其作用就是实现对容器的遍历

迭代器结构:

  1. typedef struct dictIterator {
  2. dict *d;
  3. long index;
  4. int table, safe; // safe 是否是安全的迭代器
  5. dictEntry *entry, *nextEntry;
  6. /* unsafe iterator fingerprint for misuse detection. */
  7. long long fingerprint;
  8. } dictIterator;

通过safe来区分 safe=1为安全迭代器

entry与nextEntry两个指针分别指向Hash冲突后的两个父子节点。 如果在安全模式下, 删除了entry节点, nextEntry字段可以保证后续迭代数据不丢失

迭代器的种类

普通的迭代器:只遍历数据

安全的迭代器:遍历的同时删除数据

迭代器的操作:

获取迭代器

获取普通迭代器:

  1. dictIterator *dictGetIterator(dict *d)
  2. {
  3. dictIterator *iter = zmalloc(sizeof(*iter));
  4. iter->d = d;
  5. iter->table = 0;
  6. iter->index = -1;
  7. iter->safe = 0;
  8. iter->entry = NULL;
  9. iter->nextEntry = NULL;
  10. return iter;
  11. }

不允许对数据进行操作,因为遍历迭代器时会获取dictGetIterator 会获取dictFingerprint ,时整个字典的指纹,如果遍历完成有指纹发生变化,则会导致错误。

这时你可能会问,为啥不是单线程的吗,为什么遍历有可能被其他修改了呢?其实,如果你仔细看,我们对字典的任何插入,查找等操作,都会调用rehashstep,这就有可能导致了字典指纹的变化。

获取安全迭代器:

其实只是相对普通迭代器设置safe标志为1

  1. dictIterator *dictGetSafeIterator(dict *d) {
  2. dictIterator *i = dictGetIterator(d);
  3. i->safe = 1;
  4. return i;
  5. }

字典的增删改查操作会调用dictRehashStep函数进行渐进式rehash操作,但是我们看到

  1. static void _dictRehashStep(dict *d) {
  2. if (d->iterators == 0) dictRehash(d,1);
  3. }

其实,rehash的时候,会判断是不是在安全迭代器模式,如果是则不会rehash,所以就不会导致重复遍历。

迭代器的遍历

  1. dictEntry *dictNext(dictIterator *iter)
  2. {
  3. while (1) {
  4. if (iter->entry == NULL) {
  5. dictht *ht = &iter->d->ht[iter->table];
  6. if (iter->index == -1 && iter->table == 0) {
  7. if (iter->safe)
  8. iter->d->iterators++;
  9. else
  10. iter->fingerprint = dictFingerprint(iter->d);
  11. }
  12. iter->index++;
  13. if (iter->index >= (long) ht->size) {
  14. if (dictIsRehashing(iter->d) && iter->table == 0) {
  15. iter->table++;
  16. iter->index = 0;
  17. ht = &iter->d->ht[1];
  18. } else {
  19. break;
  20. }
  21. }
  22. iter->entry = ht->table[iter->index];
  23. } else {
  24. iter->entry = iter->nextEntry;
  25. }
  26. if (iter->entry) {
  27. /* We need to save the 'next' here, the iterator user
  28. * may delete the entry we are returning. */
  29. iter->nextEntry = iter->entry->next;
  30. return iter->entry;
  31. }
  32. }
  33. return NULL;
  34. }

如果是安全模式:首次遍历iterators 会++ ,表示现在字典在安全遍历,会禁止了rehash

如果不是安全模式:首次则要记录字典的指纹信息。

字典指纹的获取:其实就是将字典转换为一个64位的数组,也可以理解是一种hash

  1. long long dictFingerprint(dict *d) {
  2. long long integers[6], hash = 0;
  3. int j;
  4. integers[0] = (long) d->ht[0].table;
  5. integers[1] = d->ht[0].size;
  6. integers[2] = d->ht[0].used;
  7. integers[3] = (long) d->ht[1].table;
  8. integers[4] = d->ht[1].size;
  9. integers[5] = d->ht[1].used;
  10. /* We hash N integers by summing every successive integer with the integer
  11. * hashing of the previous sum. Basically:
  12. *
  13. * Result = hash(hash(hash(int1)+int2)+int3) ...
  14. *
  15. * This way the same set of integers in a different order will (likely) hash
  16. * to a different number. */
  17. for (j = 0; j < 6; j++) {
  18. hash += integers[j];
  19. /* For the hashing step we use Tomas Wang's 64 bit integer hash. */
  20. hash = (~hash) + (hash << 21); // hash = (hash << 21) - hash - 1;
  21. hash = hash ^ (hash >> 24);
  22. hash = (hash + (hash << 3)) + (hash << 8); // hash * 265
  23. hash = hash ^ (hash >> 14);
  24. hash = (hash + (hash << 2)) + (hash << 4); // hash * 21
  25. hash = hash ^ (hash >> 28);
  26. hash = hash + (hash << 31);
  27. }
  28. return hash;
  29. }

释放迭代器

  1. void dictReleaseIterator(dictIterator *iter)
  2. {
  3. if (!(iter->index == -1 && iter->table == 0)) {
  4. if (iter->safe)
  5. iter->d->iterators--;
  6. else
  7. assert(iter->fingerprint == dictFingerprint(iter->d));
  8. }
  9. zfree(iter);
  10. }

释放迭代器要将安全模式下iterators++的值减回去,如果不是安全模式,则要判断字典是不是遍历的过程中被修改,修改则触发断言。

最后释放迭代器。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/735665
推荐阅读
相关标签
  

闽ICP备14008679号