前言

了解完同步删除和异步删除你将对Redis的认识会更上一层楼。加油

名次解释 :

  • Redis存储数据的k-v结构是用字典实现的。k对应的过期时间的存储也是用字典实现。
  • 键空间 : 用于存储数据库的k-v数据。
  • 过期键空间 : 用于存储数据库过期的k-v数据。

例如 : setex name 60 zhangsan 这个命令的name-zhangsan的k-v是存储在键空间的字典里。
60秒的过期时间name-60是存储在过期空间的字典里。

一、同步和异步指的是什么?

同步删除 : 删除key时释放value空间是在主线程中执行。
异步删除 : 删除key时释放value空间是在异步线程中执行。

二、代码实现

// 同步删除就很简单了,直接在字典里进行切断引用并释放空间
int dbSyncDelete(redisDb *db, robj *key) {
    if (dictSize(db->expires) > 0) dictDelete(db->expires,key->ptr);
    if (dictDelete(db->dict,key->ptr) == DICT_OK) {
        // 如果是集群,删除对应的槽分配数据
        if (server.cluster_enabled) slotToKeyDel(key);
        return 1;
    } else {
        return 0;
    }
}
// 异步删除
int dbAsyncDelete(redisDb *db, robj *key) {
    // 同步删除过期键空间key数据,并释放空间
    if (dictSize(db->expires) > 0) dictDelete(db->expires, key->ptr);
    // 因为数据库使用字典实现的,所以把存储k-v数据的dictEntry节点取出来,
    // 并且在取出来的同时,切断这个节点和键空间的引用。
    // 所以称把键空间存储的key-value删除,但是未释放key-value空间
    dictEntry *de = dictUnlink(db->dict, key->ptr);
    if (de) {
        robj *val = dictGetVal(de);
        // 获取value的大小(这个大小不是占用的字节数,而是redis内部定义的算法,根据不同类型规定的大小)
        size_t free_effort = lazyfreeGetFreeEffort(val);
        // 只有大小大于64才进行异步释放,小于64进行同步释放空间。
        // 这里就说明了,异步删除只是对value的空间进行异步释放。
        if (free_effort > LAZYFREE_THRESHOLD) {
            bioCreateBackgroundJob(BIO_LAZY_FREE,val,NULL,NULL);
            // 这里把数据节点de的value设置成null,因为value的空间释放交给异步线程去做了。
            dictSetVal(db->dict,de,NULL);
        }
    }
    if (de) {
        // 释放节点的key-value空间,如果上面已经进行异步删除了,这里
        // 释放节点的key空间
        dictFreeKey(d, he);
        // 释放节点的value空间,如果上面已经进行异步删除释放了,这里就什么都不做。
        dictFreeVal(d, he);
        // 释放节点空间
        zfree(he);
        // 如果是集群,删除对应的槽分配数据
        if (server.cluster_enabled) slotToKeyDel(key);
        return 1;
    } else {
        return 0;
    }
}
// 计算对象的大小,用于判断是否可以异步释放空间。这个大小是从需要申请多少次空间来定义的。
// 对于为什么每种类型还要判断实现的数据结构?
// 因为Redis的每中类型的底层存储的数据结构都不止一种。
size_t lazyfreeGetFreeEffort(robj *obj) {
    // list 取链表长度
    if (obj->type == OBJ_LIST) {
        return obj->ptr->len;
    // set并且数据结构是字典,取字典长度
    } else if (obj->type == OBJ_SET && obj->encoding == OBJ_ENCODING_HT) {
        return dictSize(obj->ptr);
    // zset并且数据结构是跳跃链表,取跳表长度
    } else if (obj->type == OBJ_ZSET && obj->encoding == OBJ_ENCODING_SKIPLIST){
        return obj->ptr->zsl->length;
    // hash并且数据结构是字典,取字典长度
    } else if (obj->type == OBJ_HASH && obj->encoding == OBJ_ENCODING_HT) {
        return dictSize(obj->ptr);
    } else {
        return 1; /* Everything else is a single allocation. */
    }
}

总结

同步和异步的区别就是释放value空间是主线程去执行还是异步线程去执行,理解这句话很关键。其他的操作都是由主线程执行的。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐