redis lazyfree空间懒释放的源码分析

       redis是一个内存数据库,必然就存在比较多的内存释放,然而内存释放又是一个比较重的操作,这样就会

影响redis的正常运转效率。所以,redis对于部分内存的释放采取了懒释放。空间懒释放就是将内存的释放交由

专门的线程进行释放操作。

      redis懒释放的空间主要包括三种:

      1)对象空间的释放

      2)DB空间的异步释放

      3)slots-keys空间释放

对象空间的释放

     对象释放的场景主要有del命令,过期键删除,内存淘汰等三种。对象释放是否采用懒释放取决于相关配置:
     1)del命令——server.lazyfree_lazy_server_del
     2)  过期键删除——server.lazyfree_lazy_expire 
     3)内存淘汰——lazyfree_lazy_eviction
del操作
int dbDelete(redisDb *db, robj *key) {
   return server.lazyfree_lazy_server_del ? dbAsyncDelete(db,key):dbSyncDelete(db,key);
}
过期键删除
int activeExpireCycleTryExpire(redisDb *db, dictEntry *de, long long now) {
    long long t = dictGetSignedIntegerVal(de);
    if (now > t) {
        ……
        if (server.lazyfree_lazy_expire)
            dbAsyncDelete(db,keyobj);
        else
            dbSyncDelete(db,keyobj);

    } 
}
内存淘汰
int freeMemoryIfNeeded(void) {
	……
	if (bestkey) {
            if (server.lazyfree_lazy_eviction)
                dbAsyncDelete(db,keyobj);// 异步释放
            else
                dbSyncDelete(db,keyobj);//同步释放
            ……
        }
}
对象空间懒释放
#define LAZYFREE_THRESHOLD 64
int dbAsyncDelete(redisDb *db, robj *key) {
    dictEntry *de = dictUnlink(db->dict,key->ptr);
    if (de) {
        robj *val = dictGetVal(de);
        size_t free_effort = lazyfreeGetFreeEffort(val);//计算释放对象的大小
        //释放对象的大小大于LAZYFREE_THRESHOLD,放入异步线程组的队列,异步释放
        if (free_effort > LAZYFREE_THRESHOLD) {
            atomicIncr(lazyfree_objects,1,lazyfree_objects_mutex);
            bioCreateBackgroundJob(BIO_LAZY_FREE,val,NULL,NULL);
            dictSetVal(db->dict,de,NULL);
        }
    }//释放对象小于LAZYFREE_THRESHOLD,直接同步释放
    if (de) {
        dictFreeUnlinkedEntry(db->dict,de);
        if (server.cluster_enabled) slotToKeyDel(key);
        return 1;
    } 
}

DB空间的懒释放

         当需要异步删除DB的时候,redis会申请新的哈希表作为新的DB,原DB内存的释放操作交给异步任务处理
线程执行。
//清空DB
long long emptyDb(int dbnum, int flags, void(callback)(void*)) {
    int j, async = (flags & EMPTYDB_ASYNC);
    ……
    for (j = 0; j < server.dbnum; j++) {
        if (dbnum != -1 && dbnum != j) continue;
        removed += dictSize(server.db[j].dict);
        if (async) {
            emptyDbAsync(&server.db[j]);
        } 
        ……
    }
    ……
    return removed;
}//异步清空DB
void emptyDbAsync(redisDb *db) {
    dict *oldht1 = db->dict, *oldht2 = db->expires;
    db->dict = dictCreate(&dbDictType,NULL);//创建新的DB
    db->expires = dictCreate(&keyptrDictType,NULL);
    atomicIncr(lazyfree_objects,dictSize(oldht1),
        lazyfree_objects_mutex);
    //放入异步任务处理线程队列,异步释放DB的内存空间
    bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,oldht1,oldht2);
}

slots-keys空间释放

          在redis cluster模式下,slots-keys用于记录键值对存放的slot对映关系,由跳跃表数据结构保存。当redis 
cluster需要清空slots-keys的时候,会先创建一个新的跳跃表作为新的slots-keys,然后将老的slots-keys释放工
作交给异步任务处理线程。
long long emptyDb(int dbnum, int flags, void(callback)(void*)) {
    int j, async = (flags & EMPTYDB_ASYNC);
    ……
    if (server.cluster_enabled) {
        if (async) {// 异步清空slots-key
            slotToKeyFlushAsync();
        } else {
            slotToKeyFlush();
        }
    }
}
void slotToKeyFlushAsync(void) {
    zskiplist *oldsl = server.cluster->slots_to_keys;
    server.cluster->slots_to_keys = zslCreate();
    atomicIncr(lazyfree_objects,oldsl->length,
        lazyfree_objects_mutex);
    //放入异步任务处理线程队列,异步释放slots_to_keys的内存空间
    bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,NULL,oldsl);
}




你可能感兴趣的:(redis,redis源码)