加入收藏 | 设为首页 | 会员中心 | 我要投稿 拼字网 - 核心网 (https://www.hexinwang.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长学院 > MySql教程 > 正文

如履薄冰:Redis懒惰删除的巨大牺牲

发布时间:2019-01-10 18:07:46 所属栏目:MySql教程 来源:老钱
导读:大家都知道 Redis 是单线程的,但是 Redis 4.0 增加了懒惰删除功能,懒惰删除需要使用异步线程对已删除的节点进行内存回收,这意味着 Redis 底层其实并不是单线程,它内部还有几个额外的鲜为人知的辅助线程。 这几个辅助线程在 Redis 内部有一个特别的名称

接下来我们继续追踪普通对象的异步删除 lazyfreeFreeObjectFromBioThread 是如何进行的,请仔细阅读代码注释。

  1. void lazyfreeFreeObjectFromBioThread(robj *o) {  
  2.     decrRefCount(o); // 降低对象的引用计数,如果为零,就释放  
  3.     atomicDecr(lazyfree_objects,1); // lazyfree_objects 为待释放对象的数量,用于统计  
  4. }  
  5. // 减少引用计数  
  6. void decrRefCount(robj *o) {  
  7.     if (o->refcount == 1) {  
  8.         // 该释放对象了  
  9.         switch(o->type) {  
  10.         case OBJ_STRING: freeStringObject(o); break;  
  11.         case OBJ_LIST: freeListObject(o); break;  
  12.         case OBJ_SET: freeSetObject(o); break;  
  13.         case OBJ_ZSET: freeZsetObject(o); break;  
  14.         case OBJ_HASH: freeHashObject(o); break;  // 释放 hash 对象,继续追踪  
  15.         case OBJ_MODULE: freeModuleObject(o); break;  
  16.         case OBJ_STREAM: freeStreamObject(o); break;  
  17.         default: serverPanic("Unknown object type"); break;  
  18.         } 
  19.         zfree(o);  
  20.     } else {  
  21.         if (o->refcount <= 0) serverPanic("decrRefCount against refcount <= 0");  
  22.         if (o->refcount != OBJ_SHARED_REFCOUNT) o->refcount--; // 引用计数减 1  
  23.     }  
  24. }  
  25. // 释放 hash 对象  
  26. void freeHashObject(robj *o) {  
  27.     switch (o->encoding) {  
  28.     case OBJ_ENCODING_HT:  
  29.         // 释放字典,我们继续追踪  
  30.         dictRelease((dict*) o->ptr);  
  31.         break;  
  32.     case OBJ_ENCODING_ZIPLIST:  
  33.         // 如果是压缩列表可以直接释放  
  34.         // 因为压缩列表是一整块字节数组  
  35.         zfree(o->ptr);  
  36.         break;  
  37.     default:  
  38.         serverPanic("Unknown hash encoding type");  
  39.         break;  
  40.     }  
  41. }  
  42. // 释放字典,如果字典正在迁移中,ht[0] 和 ht[1] 分别存储旧字典和新字典  
  43. void dictRelease(dict *d)  
  44. {  
  45.     _dictClear(d,&d->ht[0],NULL); // 继续追踪  
  46.     _dictClear(d,&d->ht[1],NULL);  
  47.     zfree(d);  
  48. }  
  49. // 这里要释放 hashtable 了  
  50. // 需要遍历第一维数组,然后继续遍历第二维链表,双重循环  
  51. int _dictClear(dict *d, dictht *ht, void(callback)(void *)) {  
  52.     unsigned long i;  
  53.     /* Free all the elements */  
  54.     for (i = 0; i < ht->size && ht->used > 0; i++) {  
  55.         dictEntry *he, *nextHe;  
  56.         if (callback && (i & 65535) == 0) callback(d->privdata);  
  57.         if ((he = ht->table[i]) == NULL) continue;  
  58.         while(he) {  
  59.             nextHe = he->next;  
  60.             dictFreeKey(d, he); // 先释放 key  
  61.             dictFreeVal(d, he); // 再释放 value  
  62.             zfree(he); // 最后释放 entry  
  63.             ht->used--;  
  64.             he = nextHe;  
  65.         }  
  66.     }  
  67.     /* Free the table and the allocated cache structure */  
  68.     zfree(ht->table); // 可以回收第一维数组了  
  69.     /* Re-initialize the table */  
  70.     _dictReset(ht);  
  71.     return DICT_OK; /* never fails */  

这些代码散落在多个不同的文件,我将它们凑到了一块便于读者阅读。从代码中我们可以看到释放一个对象要深度调用一系列函数,每种对象都有它独特的内存回收逻辑。

04.5.9.4 队列安全

(编辑:拼字网 - 核心网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!