Source code analysis of redis lazy free space lazy release

Posted by jay7981 on Tue, 09 Jul 2019 22:44:56 +0200

redis is a memory database, there must be more memory release, but memory release is a heavy operation, so it will be

It affects the normal operation efficiency of redis. So redis lazily releases some memory. Spatial lazy release is the release of memory by hand

Special threads perform release operations.

There are three main types of space released by redis laziness: redis laziness, redis laziness, redis laziness, redis laziness, redis laziness, redis laziness, redis laziness and redis laziness.

1) Release of object space

2) Asynchronous release of DB space

3) slots-keys space release

Release of Object Space

Object release scenarios include del command, deletion of expired keys, memory obsolescence and so on. Whether object release is lazy depends on the configuration:
Del command - server.lazyfree_lazy_server_del
2) Delete expired keys - server.lazyfree_lazy_expire
3) Memory Elimination - lazyfree_lazy_eviction
del operation
int dbDelete(redisDb *db, robj *key) {
   return server.lazyfree_lazy_server_del ? dbAsyncDelete(db,key):dbSyncDelete(db,key);
}
Delete expired keys
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);

    } 
}
Memory obsolescence
int freeMemoryIfNeeded(void) {
	......
	if (bestkey) {
            if (server.lazyfree_lazy_eviction)
                dbAsyncDelete(db,keyobj);// Asynchronous release
            else
                dbSyncDelete(db,keyobj);//Synchronized release
            ......
        }
}
Lazy Release of Object Space
#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);//Calculate the size of the released object
        //Release object size is larger than LAZYFREE_THRESHOLD, put in the queue of asynchronous thread group, asynchronous release
        if (free_effort > LAZYFREE_THRESHOLD) {
            atomicIncr(lazyfree_objects,1,lazyfree_objects_mutex);
            bioCreateBackgroundJob(BIO_LAZY_FREE,val,NULL,NULL);
            dictSetVal(db->dict,de,NULL);
        }
    }//Release object less than LAZYFREE_THRESHOLD, direct synchronous release
    if (de) {
        dictFreeUnlinkedEntry(db->dict,de);
        if (server.cluster_enabled) slotToKeyDel(key);
        return 1;
    } 
}

Lazy Release of DB Space

When DB is deleted asynchronously, redis requests a new hash table as the new DB, and the release of the original DB memory is handed over to the asynchronous task.
Thread execution.
//Clear 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;
}//Asynchronous Cleaning of DB
void emptyDbAsync(redisDb *db) {
    dict *oldht1 = db->dict, *oldht2 = db->expires;
    db->dict = dictCreate(&dbDictType,NULL);//Create a new DB
    db->expires = dictCreate(&keyptrDictType,NULL);
    atomicIncr(lazyfree_objects,dictSize(oldht1),
        lazyfree_objects_mutex);
    //Put in the asynchronous task processing thread queue and asynchronously release DB's memory space
    bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,oldht1,oldht2);
}

slots-keys space release

In redis cluster mode, slots-keys are used to record the slot mapping relationship stored by key-value pairs and are saved by jump table data structure. When redis
When cluster needs to empty slots-keys, it creates a new jump table as a new slots-keys, and then releases the old slots-keys.
Handle threads for asynchronous tasks.
long long emptyDb(int dbnum, int flags, void(callback)(void*)) {
    int j, async = (flags & EMPTYDB_ASYNC);
    ......
    if (server.cluster_enabled) {
        if (async) {// Asynchronous clearing 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);
    //Put in the asynchronous task processing thread queue and asynchronously release slots_to_keys memory space
    bioCreateBackgroundJob(BIO_LAZY_FREE,NULL,NULL,oldsl);
}




Topics: Redis Database less