udweagvoer 2017-08-25
在Redis中,对于大KEY的删除一直是个比较头疼的问题,为了不影响服务,我们通常需要自己实现工具来删除大KEY,或者在业务低峰期进行删除操作。
为了解决以上问题, Redis 4.0 新添加了 UNLINK 命令用于执行大KEY异步删除。那么这个异步删除的背后的逻辑是什么?
通过源码我们可以的得知以下信息:
当我们调用异步删除UNLINK命令时:
异步删除代码如下 :
int dbAsyncDelete(redisDb *db, robj *key) { /* */ if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr); /* 在Main Dict 链表去掉引用,得到K-V entryDict */ dictEntry *de = dictUnlink(db->dict,key->ptr); if (de) { robj *val = dictGetVal(de); size_t free_effort = lazyfreeGetFreeEffort(val); /* 计算DEL key 的代价,根据代价决定是否采用异步删除方式 */ if (free_effort > LAZYFREE_THRESHOLD) { atomicIncr(lazyfree_objects,1,lazyfree_objects_mutex); bioCreateBackgroundJob(BIO_LAZY_FREE,val,NULL,NULL); dictSetVal(db->dict,de,NULL); } } /* 释放K-V空间,或者采用了异步删除方式,只需要释放Key空间 */ if (de) { dictFreeUnlinkedEntry(db->dict,de); if (server.cluster_enabled) slotToKeyDel(key); return 1; } else { return 0; } } /* 释放LIST 空间 */ void quicklistRelease(quicklist *quicklist) { unsigned long len; quicklistNode *current, *next; current = quicklist->head; len = quicklist->len; while (len--) { next = current->next; zfree(current->zl); quicklist->count -= current->count; zfree(current); quicklist->len--; current = next; } zfree(quicklist); } /* 释放HASH表空间 */ static int _dictClear(dict *ht) { unsigned long i; for (i = 0; i < ht->size && ht->used > 0; i++) { dictEntry *he, *nextHe; if ((he = ht->table[i]) == NULL) continue; while(he) { nextHe = he->next; dictFreeEntryKey(ht, he); dictFreeEntryVal(ht, he); free(he); ht->used--; he = nextHe; } } free(ht->table); _dictReset(ht); return DICT_OK; /* never fails */ }
由于异步删除实际上是先在MAIN DICT 里边把 这个K,V 的引用关系去掉了,所以当我们再次查询这个Key 的时候是查不到的,然后在慢慢释放Value 所占用的内存空间。
我们发现在异步进行删除的时候,不管是删除 HASH也好,还是QUICKLIST 也罢,这部分其实并没有进行一个速度的控制,只是起了一个线程让他去删除,能跑多快就跑多快,这样可能会导致我们在进行删除的时候CPU飙高。
这个删除大KEY是在Master 上进行的,如果这个节点有Slave呢?slave 会进行怎样的操作?同样根据代码可以发现,我们在执行UNLINK操作时,实际上在 AOF 和 通知Slave的时候只是发送了一条DEL xxkey 命令,当slave 收到del命令时,会采取以上同样的判断对这个key进行删除。
notifyKeyspaceEvent(NOTIFY_GENERIC,"del",c->argv[j],c->db->id);
http://www.cnblogs.com/svan/p/7054129.html