接上一篇,我们得知了redis中存在大KEY,那么这个大KEY如何删除呢?本文将从源码角度分析Redis4.0带来的新特性。

在Redis中,对于大KEY的删除一直是个比较头疼的问题,为了不影响服务,我们通常需要自己实现工具来删除大KEY,或者在业务低峰期进行删除操作。 
为了解决以上问题, Redis 4.0 新添加了 UNLINK 命令用于执行大KEY异步删除。那么这个异步删除的背后的逻辑是什么?

通过源码我们可以的得知以下信息: 
当我们调用异步删除UNLINK命令时:

  1. 释放掉Expire Dicti 对 K-V 的引用
  2. 释放Main Dict 对 K-V 的引用,同时记录下这个K-V 的 Entry地址
  3. 计算释放掉这个V 所需要的代价,计算方法如下: 
    3.1 如果这个V 是一个 String 类型,则代价为 1 
    3.2 如果这个V 是一个复合类型,则代价为 该复合类型的长度,比如,list 则为 llen 的结果,hash 则为 hlen 的结果 …
  4. 根据得到的代价值,和代价阈值比对,如果小于 64 则,可以直接释放掉K-V 内存空间;如果大于 64 则,把该V 放入lazyfree 队列中,同时启动一个BIO后台JOB进行删除 
    4.1 在后台线程对 V 进行删除时,也是根据不同类型的 V 做不同的操作 
    4.2 如果是 LIST 类型,则根据LIST 长度,则直接释放空间。 
    4.3 如果是 SET 类型,并且数据结构采用 HASH 表存储,那么遍历整个hash表,逐个释放 k,v空间;如果数据结构采用 intset,则直接释放空间即可 
    4.4 如果是 ZSET 类型,并且数据结构采用 SKIPLIST 存储,由于 SKIPLIST 底层采用 HASH + skiplist 存储,那么会先释放掉 SKIPLIST 中 hash 存储空间,再释放掉 SKIPLIST 中 skiplist 部分; 如果数据结构采用 ZIPLIST 存储,则直接释放空间。 
    4.5 如果是 HASH 类型,并且数据结构采用 HASH表存储,则遍历整个hash表,逐个释放 k,v空间;如果数据结构采用 ZIPLIST 存储,则直接释放空间。
  5. 设置 V 值等于NULL
  6. 释放掉 K-V 空间

异步删除代码如下 :

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);

Redis4.0新特性之-大KEY删除的更多相关文章

  1. Redis4.0新特性

    redis 4.0 新特性 Redis 4.0在2017年7月发布为GA.包含几个重大改进:更好的复制(PSYNC2),线程DEL / FLUSH,混合RDB + AOF格式,活动内存碎片整理,内存使 ...

  2. Redis4.0新特性(一)-Memory Command

    Redis4.0版本增加了很多诱人的新特性,在redis精细化运营管理中都非常有用(猜想和antirez加入redislabs有很大关系):此系列几篇水文主要介绍以下几个新特性的使用和效果. Redi ...

  3. Redis4.0新特性 -Lazy Free

    Redis4.0新增了非常实用的lazy free特性,从根本上解决Big Key(主要指定元素较多集合类型Key)删除的风险.笔者在redis运维中也遇过几次Big Key删除带来可用性和性能故障. ...

  4. 【特性】Redis4.0新特性

    模块系统 Redis 4.0 发生的最大变化就是加入了模块系统, 这个系统可以让用户通过自己编写的代码来扩展和实现 Redis 本身并不具备的功能, 具体使用方法可以参考 antirez 的博文< ...

  5. Redis 6.0 新特性-多线程连环13问!

    Redis 6.0 来了 在全国一片祥和IT民工欢度五一节假日的时候,Redis 6.0不声不响地于5 月 2 日正式发布了,吓得我赶紧从床上爬起来,学无止境!学无止境! 对于6.0版本,Redis之 ...

  6. redis5.0新特性

    1. redis5.0新特性 1.1. 新的Stream类型 1.1.1. 什么是Stream数据类型 抽象数据日志 数据流 1.2. 新的Redis模块API:Timers and Cluster ...

  7. Day07 jdk5.0新特性&Junit&反射

    day07总结 今日内容 MyEclipse安装与使用 JUnit使用 泛型 1.5新特性 自动装箱拆箱 增强for 静态导入 可变参数方法 枚举 反射 MyEclipse安装与使用(yes) 安装M ...

  8. [翻译] C# 8.0 新特性 Redis基本使用及百亿数据量中的使用技巧分享(附视频地址及观看指南) 【由浅至深】redis 实现发布订阅的几种方式 .NET Core开发者的福音之玩转Redis的又一傻瓜式神器推荐

    [翻译] C# 8.0 新特性 2018-11-13 17:04 by Rwing, 1179 阅读, 24 评论, 收藏, 编辑 原文: Building C# 8.0[译注:原文主标题如此,但内容 ...

  9. 【mysql】mysq8.0新特性

    一.MySQL8.0简介   mysql8.0现在已经发布,2016-09-12第一个DM(development milestone)版本8.0.0发布.新的版本带来很多新功能和新特性,对性能也得到 ...

随机推荐

  1. 蓝桥杯历届试题 危险系数(dfs或者并查集求无向图关于两点的割点个数)

    Description 抗日战争时期,冀中平原的地道战曾发挥重要作用. 地道的多个站点间有通道连接,形成了庞大的网络.但也有隐患,当敌人发现了某个站点后,其它站点间可能因此会失去联系. 我们来定义一个 ...

  2. 数字IC设计入门书单

    首发于观芯志 写文章     数字IC设计入门书单 Forever snow   1 年前 作者:Forever snow链接:你所在领域的入门书单? - 知乎用户的回答来源:知乎著作权归作者所有,转 ...

  3. 大数据入门第二十五天——logstash入门

    一.概述 1.logstash是什么 根据官网介绍: Logstash 是开源的服务器端数据处理管道,能够同时 从多个来源采集数据.转换数据,然后将数据发送到您最喜欢的 “存储库” 中.(我们的存储库 ...

  4. 20155216 Exp9 Web安全基础实践

    Exp9 Web安全基础实践 基础问题回答 1.SQL注入攻击原理,如何防御? 1.对用户的输入进行校验,可以通过正则表达式,双"-"进行转换等. 2.不要使用动态拼装sql,可以 ...

  5. Unused Method(不再使用的方法)——Dead Code(死亡代码)

        系列文章目录:     使用Fortify进行代码静态分析(系列文章) Unused Method(不再使用的方法)    示例:  private bool checkLevel(strin ...

  6. Microsoft Dynamics CRM 增删改子表汇总子表的某个字段到主表的某个字段(通用插件)

    背景 经常有某个汇总子表的数量到主表的总数量,或者汇总子表的总价到主表的总价这种需求. 传统的做法: 1.就是为每个子表实体单独写成一个插件,但是这样不好复用. 2.主表的汇总字段是汇总货币类型,但是 ...

  7. Linux下tomcat的启动,关闭,以及shutdown失败杀死进程的方法

    1.tomcat服务器第一次启动并查看启动日志的命令 在 ../bin 文件夹下输入./startup.sh;tail -f ../logs/catalina.out 2.需要重启服务器的时候 在 . ...

  8. 2、Arx二次开发创建第一个应用程序

    一.本节课程 Arx二次开发创建第一个应用程序 二.本节要讲解的知识点 1.手动创建ARX的应用的步骤. 2.应用向导创建ARX应用程序的步骤. 三.具体内容 1.需求:创建一个Hello World ...

  9. 【翻译】Brewer's CAP Theorem CAP定理

    Brewer's CAP Theorem 原文地址:http://www.julianbrowne.com/article/brewers-cap-theorem Brewer’s (CAP) The ...

  10. Teaching Machines to Understand Us 让机器理解我们 之二 深度学习的历史

    Deep history 深度学习的历史 The roots of deep learning reach back further than LeCun’s time at Bell Labs. H ...