This article from the @ where the fun technology Pesiwang classmate contribution to share, Redis Rdb file persistence of the internal implementation of the source code analysis.
This article analyzes the source code based on Redis 2.4.7 stable version. The following is the original article:
RDB is one way in which Redis saves memory data to disk data (the other is aof). The main principle of RDB is to save a snapshot of all the data in memory to disk at a point in time. Save data snapshots are implemented by fork a child process to write the data in memory to a temporary file when the condition is reached. After all the data has been written, the temporary file is renamed to the target Rdb file with the Atomic function rename (2). This implementation leverages fork copy on write.
The other is to actively trigger the save data snapshot through the Save command, which is blocking, that is, the dataset snapshot is not saved by generating the child process. Related configuration
Save <seconds> <changes>
After how many seconds and how many keys have changed, you can configure multiple, as long as there is a meeting to save data snapshot to disk
Rdbcompression Yes
Whether the data is compressed when it is saved to the Rdb file, and if you do not want to configure ' no ', the default is ' yes ' because compression reduces I/O, and of course, compression consumes some CPU resources.
Dbfilename Dump.rdb
Snapshot file name
Dir./
The directory where the snapshot files reside, and the directory RDB file format where the aof files are located
[Note: This section refers to the type, the value is not specifically marked in the case of the Rdb file for the] Rdb the overall format of the file
File Signature | Version number | Type | Value | Type | Value | ... | Type | Value
[Note: Vertical bars and spaces are added for readability, RDB files are not separated by vertical bars and spaces]
The file signature is a string: Redis version number is a string: 0002 type is the type of the value, there are many types of Redis value, the following describes the value is the corresponding type of value, different types of value format. The value here contains the key and Val in the Redis. Instead of a single finger Redis val. Redis_selectdb type and redis_eof type Redis_selectdb type: The corresponding value is the number of Redis db, starting from 0 to a number smaller than DB 1. The DB number can be configured in Redis, and each key belongs to only one db. The number used to store the Redis DB is in the format used when storing the length, in order to compress the Rdb file, the number of bytes used in the storage length is not the same, see the storage redis_eof type of the length in the bottom rdb: there is no corresponding value. The Terminator of the Rdb file.
By substituting this redis_selectdb type and redis_eof type into the Rdb file above, the overall format of the Rdb file becomes:
File Signature | Version number | REDIS_SELECTDB Type | DB number | Type | Value | ... | REDIS_SELECTD Type | DB number | Type | Value | ... | Redis_eof type data that precedes each DB number and the next Redis_selectdb type appears to be the key and value below that DB
Related code
rdb.c:394
int rdbSave(char *filename) {
…
fp = fopen(tmpfile,"w");
if (!fp) {
redisLog(REDIS_WARNING, "Failed saving the DB: %s", strerror(errno));
return REDIS_ERR;
}
if (fwrite("REDIS0002",9,1,fp) == 0) goto werr;
for (j = 0; j < server.dbnum; j ) {
…
/* Write the SELECT DB opcode */
if (rdbSaveType(fp,REDIS_SELECTDB) == -1) goto werr;
if (rdbSaveLen(fp,j) == -1) goto werr;
/* Iterate this DB writing every entry */
while((de = dictNext(di)) != NULL) {
…
initStaticStringObject(key,keystr);
expiretime = getExpire(db,&key);
/* Save the expire time */
if (expiretime != -1) {
/* If this key is already expired skip it */
if (expiretime < now) continue;
if (rdbSaveType(fp,REDIS_EXPIRETIME) == -1) goto werr;
if (rdbSaveTime(fp,expiretime) == -1) goto werr;
}
/* Save the key and associated value. This requires special
* handling if the value is swapped out. */
if (!server.vm_enabled || o->storage == REDIS_VM_MEMORY ||
o->storage == REDIS_VM_SWAPPING) {
int otype = getObjectSaveType(o);
/* Save type, key, value */
if (rdbSaveType(fp,otype) == -1) goto werr;
if (rdbSaveStringObject(fp,&key) == -1) goto werr;
if (rdbSaveObject(fp,o) == -1) goto werr;
} else {
/* REDIS_VM_SWAPPED or REDIS_VM_LOADING */
robj *po;
/* Get a preview of the object in memory */
po = vmPreviewObject(o);
/* Save type, key, value */
if (rdbSaveType(fp,getObjectSaveType(po)) == -1)
goto werr;
if (rdbSaveStringObject(fp,&key) == -1) goto werr;
if (rdbSaveObject(fp,po) == -1) goto werr;
/* Remove the loaded object from memory */
decrRefCount(po);
}
}
dictReleaseIterator(di);
}
/* EOF opcode */
if (rdbSaveType(fp,REDIS_EOF) == -1) goto werr;
…
}
Storage of length in RDB
Redis in order to compress Rdb file is really painstaking effort, first look at Redis in order to compress the length of use storage. The length is mainly used in string length, chain table length, hash table size storage.
Redis the length of storage into four types, the leftmost byte of the first two bits from left to right to distinguish between the length of the storage type. A type bit represents a type integer representing a byte-occupied type resolution 00 0 1 when the length can be used in 6-bit notation Use this type 01 1 2 when length is not available 6-bit representation and can use 14-bit notation for this type 10 2 5 When the length cannot be represented in 14 digits and can be used in 32-bit notation
Related code
Rdb.c:31
int Rdbsavelen (FILE *fp, uint32_t len) {
unsigned char buf[2];
int Nwritten;
if (Len < (1<<6)) {/
* Save a 6 bit len *
/buf[0] = (LEN&0XFF) | ( REDIS_RDB_6BITLEN<<6);
if (Rdbwriteraw (fp,buf,1) = = 1) return-1;
Nwritten = 1;
} else if (Len < (1<<14)) {/
* Save a bit len */
Buf[0] = ((len>>8) &0xff) | ( REDIS_RDB_14BITLEN<<6);
BUF[1] = len&0xff;
if (Rdbwriteraw (fp,buf,2) = = 1) return-1;
Nwritten = 2;
} else {
/* Save a bit len *
/buf[0] = (redis_rdb_32bitlen<<6);
if (Rdbwriteraw (fp,buf,1) = = 1) return-1;
Len = htonl (len);
if (Rdbwriteraw (fp,&len,4) = = 1) return-1;
Nwritten = 1 4;
}
return nwritten;
}
Perhaps you have found that there are only 3 kinds of tables on the top, and there is a kind of where to go.
This particular release is because of this comparison special type bit indicates that the type integer represents the byte after 6-bit meaning type resolution 11 3 encoding type if the string is stored by encoding, the bits of the type storing the length are represented as 11, and then the following 6-bit encoding type is determined to read and parse the next data
Do you think that this type of length is very strange, why do this.
Redis need to encode stored content in two cases
1. Convert a string to an integer storage
For example: ' 100 ' requires 4 bytes of storage, conversion integer only need one byte
Correlation function rdbtryintegerencoding (rdb.c:88)
2. Compressing strings using the LZF algorithm
Correlation function lzf_compress (lzf_c.c:99), Lzf algorithm interpretation see LZF string compression algorithm
When Redis uses these two encodings to encode strings, it is necessary to distinguish between the strings being encoded and the encoded strings specially processed, because the length information is stored in front of the string, so you can add the encoded type of information to the location of the storage length.
Let's take a look at the relevant code
rdb.c:557
uint32_t rdbLoadLen(FILE *fp, int *isencoded) {
unsigned char buf[2];
uint32_t len;
int type;
if (isencoded) *isencoded = 0;
if (fread(buf,1,1,fp) == 0) return REDIS_RDB_LENERR;
type = (buf[0]&0xC0)>>6;
if (type == REDIS_RDB_6BITLEN) {
/* Read a 6 bit len */
return buf[0]&0x3F;
} else if (type == REDIS_RDB_ENCVAL) {
/* Read a 6 bit len encoding type */
if (isencoded) *isencoded = 1;
return buf[0]&0x3F;
} else if (type == REDIS_RDB_14BITLEN) {
/* Read a 14 bit len */
if (fread(buf 1,1,1,fp) == 0) return REDIS_RDB_LENERR;
return ((buf[0]&0x3F)<<8)|buf[1];
} else {
/* Read a 32 bit len */
if (fread(&len,4,1,fp) == 0) return REDIS_RDB_LENERR;
return ntohl(len);
}
}
We can see that when reading the RDB file, when the length type is found to be redis_rdb_encval, the encoding type is returned.
Let's take a look at the processing after you know the encoding type
rdb.c:633
RobJ *rdbgenericloadstringobject (FILE*FP, int encode) {
int isencoded;
uint32_t Len;
SDS Val;
Len = Rdbloadlen (fp,&isencoded);
if (isencoded) {
switch (len) {case
redis_rdb_enc_int8: Case
redis_rdb_enc_int16: Case
Redis_rdb_enc _int32: Return
rdbloadintegerobject (fp,len,encode);
Case REDIS_RDB_ENC_LZF: Return
rdbloadlzfstringobject (FP);
Default:
redispanic ("Unknown RDB encoding Type");
}
if (len = = Redis_rdb_lenerr) return NULL;
val = Sdsnewlen (Null,len);
if (Len && fread (val,len,1,fp) = = 0) {
sdsfree (val);
return NULL;
}
Return CreateObject (Redis_string,val);
}
Read length If the length type has encoded information, read based on the encoding type if the length type is a valid length, the string redis_expiretime type is read based on the length information if a key is set by the expire, Then there will be a redis_expiretime type corresponding to the value in front of the key and value. The value of the Redis_expiretime type corresponds to the timestamp of the expiration point Redis_expiretime type and its value are optional, not necessary, only the key that is set by the expire has this value
Assuming that a key has been set by the expire command and substituting this redis_expiretime type into the Rdb file above, the overall format of the Rdb file becomes:
File Signature | Version number | REDIS_SELECTDB Type | DB number | Redis_expiretime Type | Timestamp | Type | Value | ... | REDIS_SELECTD Type | DB number | Type | Value | ... | Redis_eof type Data type
Data types are mainly of the following types: redis_string type Redis_list type Redis_set type Redis_zset type Redis_hash type Redis_vmpointer type Redis_hash_zipmap type Redis_list_ziplist type Redis_set_intset type Redis_zset_ziplist type
Of these four data types, Redis_hash_zipmap,redis_list_ziplist,redis_set_intset and redis_zset_ziplist, are types that are only available in RDB files. The other data type is actually the value stored in the Type field in the Val object.
Below is an example of redis_string type and redis_list type, all other types are similar
Redis_string type
Assuming that a value in the Rdb file is a redis_string type, such as executing a set MyKey myval command, the Rdb file is represented as:
Redis_string Type | Value
The value contains the length of the key, the value of the key, the length of Val and the value of Val, and the format of the Redis_string type value is:
Redis_string Type | Keylen | MyKey | Vallen | Myval
Storage format for length see storage of length in RDB
Redis_list type
1.List
Redis_list | Listlen | Len | Value | Len | Value
Listlen is the length of the linked list
Len is the length of the value of the linked list node
Value is the values of the linked list node.
2.Ziplist
Redis_encoding_ziplist | Ziplist
Ziplist is implemented by strings, stored directly in RDB files, and can be saved with snapshots
Let's look at the specifics of the implementation
Whether the trigger condition is met by fork a child process to save the snapshot or triggered by the Save command, is actually called the same function Rdbsave (rdb.c:394).
Let's take a look at the realization of saving the snapshot after the trigger condition is satisfied through the implementation of fork sub process
The snapshot-saved conditions are checked in the Servercron function that is invoked every 100ms, and snapshots are saved if they are met
redis.c:604
/* Check if a background saving or AOF rewrite in progress terminated */
if (server.bgsavechildpid != -1 || server.bgrewritechildpid != -1) {
int statloc;
pid_t pid;
if ((pid = wait3(&statloc,WNOHANG,NULL)) != 0) {
if (pid == server.bgsavechildpid) {
backgroundSaveDoneHandler(statloc);
}
…
updateDictResizePolicy();
}
} else {
time_t now = time(NULL);
/* If there is not a background saving in progress check if
* we have to save now */
for (j = 0; j < server.saveparamslen; j ) {
struct saveparam *sp = server.saveparams j;
if (server.dirty >= sp->changes &&
now-server.lastsave > sp->seconds) {
redisLog(REDIS_NOTICE,"%d changes in %d seconds. Saving…",
sp->changes, sp->seconds);
rdbSaveBackground(server.dbfilename);
break;
}
}
…
}
If the back end has a rdb or write-aof subprocess, check to see if the RDB child has exited and, if it exits, perform some finishing touches, such as updating the dirty data count Server.dirty and the most recent snapshot save time Server.lastsave. If the back end does not have a child process that writes RDB and does not have a child process that writes the AOF, it is determined that the condition that triggers the write Rdb is satisfied and, if the condition is met, the snapshot is saved by calling the Rdbsavebackground function.
Follow the Rdbsavebackground function inside to see
rdb.c:499
int Rdbsavebackground (char *filename) {pid_t childpid;
Long long start;
if (server.bgsavechildpid!=-1) return redis_err;
if (server.vm_enabled) waitemptyiojobsqueue ();
Server.dirty_before_bgsave = Server.dirty;
Start = Ustime ();
if ((Childpid = fork ()) = = 0) {/* Child */if (server.vm_enabled) vmreopenswapfile ();
if (server.ipfd > 0) Close (SERVER.IPFD);
if (Server.sofd > 0) Close (SERVER.SOFD);
if (rdbsave (filename) = = REDIS_OK) {_exit (0);
else {_exit (1);
} else {/* Parent/Casino server.stat_fork_time = Ustime ()-start;
if (childpid = = 1) {Redislog (redis_warning, "Can" T Save in Background:fork:%s ", Strerror (errno));
return redis_err;
} redislog (Redis_notice, "Background saving started by PID%d", childpid);
Server.bgsavechildpid = Childpid;
Updatedictresizepolicy ();
return REDIS_OK; return REDIS_OK; /* unreached */}
A child process with write Rdb is judged, and an error is returned if there is already a child process to save the snapshot. If virtual memory is started, wait for all process swap-swapping task threads to exit, and if the VM task is processed, it will wait. All the swap out tasks are complete and all VM threads exit. Saves the current dirty data count, which is used to update the current dirty data count when the snapshot is saved (see function backgroundsavedonehandler,rdb.c:1062) write down the current time to count the time that a process takes fork fork a word process, Child processes call Rdbsave for snapshots Save the parent process Statistics fork the time that a subprocess consumes: Server.stat_fork_time = Ustime ()-start, this statistic can be obtained by the info command. The policy of saving the subprocess ID and updating the incremental hash is that no incremental hash should be made at this point, otherwise a large number of key changes may result in a large number of fork copy-on-write.
Here we know that Rdb's snapshot save is implemented by the function Rdbsave function (rdb.c:394). In fact, the Save command is also implemented by calling this function. Let's take a quick look.
db.c:323
void Savecommand (Redisclient *c) {
if (server.bgsavechildpid!=-1) {
addreplyerror (c, "Background save already In progress ");
return;
}
if (Rdbsave (server.dbfilename) = = REDIS_OK) {
addreply (C,shared.ok);
} else {
addreply (c,shared.err);
}
Finally we go into the Rdbsave function to see
rdb.c:394
int rdbSave(char *filename) {
...
/* Wait for I/O therads to terminate, just in case this is a
* foreground-saving, to avoid seeking the swap file descriptor at the
* same time. */
if (server.vm_enabled)
waitEmptyIOJobsQueue();
snprintf(tmpfile,256,"temp-%d.rdb", (int) getpid());
fp = fopen(tmpfile,"w");
if (!fp) {
redisLog(REDIS_WARNING, "Failed saving the DB: %s", strerror(errno));
return REDIS_ERR;
}
if (fwrite("REDIS0002",9,1,fp) == 0) goto werr;
for (j = 0; j < server.dbnum; j ) {
redisDb *db = server.db j;
dict *d = db->dict;
if (dictSize(d) == 0) continue;
di = dictGetSafeIterator(d);
if (!di) {
fclose(fp);
return REDIS_ERR;
}
/* Write the SELECT DB opcode */
if (rdbSaveType(fp,REDIS_SELECTDB) == -1) goto werr;
if (rdbSaveLen(fp,j) == -1) goto werr;
/* Iterate this DB writing every entry */
while((de = dictNext(di)) != NULL) {
sds keystr = dictGetEntryKey(de);
robj key, *o = dictGetEntryVal(de);
time_t expiretime;
initStaticStringObject(key,keystr);
expiretime = getExpire(db,&key);
/* Save the expire time */
if (expiretime != -1) {
/* If this key is already expired skip it */
if (expiretime < now) continue;
if (rdbSaveType(fp,REDIS_EXPIRETIME) == -1) goto werr;
if (rdbSaveTime(fp,expiretime) == -1) goto werr;
}
/* Save the key and associated value. This requires special
* handling if the value is swapped out. */
if (!server.vm_enabled || o->storage == REDIS_VM_MEMORY ||
o->storage == REDIS_VM_SWAPPING) {
int otype = getObjectSaveType(o);
/* Save type, key, value */
if (rdbSaveType(fp,otype) == -1) goto werr;
if (rdbSaveStringObject(fp,&key) == -1) goto werr;
if (rdbSaveObject(fp,o) == -1) goto werr;
} else {
/* REDIS_VM_SWAPPED or REDIS_VM_LOADING */
robj *po;
/* Get a preview of the object in memory */
po = vmPreviewObject(o);
/* Save type, key, value */
if (rdbSaveType(fp,getObjectSaveType(po)) == -1)
goto werr;
if (rdbSaveStringObject(fp,&key) == -1) goto werr;
if (rdbSaveObject(fp,po) == -1) goto werr;
/* Remove the loaded object from memory */
decrRefCount(po);
}
}
dictReleaseIterator(di);
}
/* EOF opcode */
if (rdbSaveType(fp,REDIS_EOF) == -1) goto werr;
/* Make sure data will not remain on the OS"s output buffers */
fflush(fp);
fsync(fileno(fp));
fclose(fp);
/* Use RENAME to make sure the DB file is changed atomically only
* if the generate DB file is ok. */
if (rename(tmpfile,filename) == -1) {
redisLog(REDIS_WARNING,"Error moving temp DB file on the final destination: %s", strerror(errno));
unlink(tmpfile);
return REDIS_ERR;
}
redisLog(REDIS_NOTICE,"DB saved on disk");
server.dirty = 0;
server.lastsave = time(NULL);
return REDIS_OK;
werr:
fclose(fp);
unlink(tmpfile);
redisLog(REDIS_WARNING,"Write error saving DB on disk: %s", strerror(errno));
if (di) dictReleaseIterator(di);
return REDIS_ERR;
}
The VM thread is judged again, because the VM thread is not judged if it came through the Save command. Create and open temporary files write to file signature "Redis" and version number "0002" traverse all the keys in all db for each key, first determine whether the expiretime is set, and if it is set, save Expiretime to Rdb file. Then determine the value of the key is otherwise in memory, if it is in memory, then out to write to the Rdb file saved, if swapped out to virtual memory, then read from virtual memory and write to the Rdb file. Different types have different storage formats, detailed see RDB file format last written to RDB file terminator close file and rename temporary file name to official filename Update dirty Data Count Server.dirty 0 and most recently write Rdb file time Server.lastsave for the current time, this is only useful if triggered by the Save command. Because if you write a Rdb file by fork A subprocess, the update is not valid because the data for the child process is updated.
If you are writing a RDB file by fork a subprocess (that is, not triggered by the Save command), you may have some data changed in the process of writing the Rdb file, and then the dirty data count server.dirty how to update it. How did the Redis deal with it?
Let's take a look at the process of writing RDB.
redis.c:605
if (server.bgsavechildpid!=-1 | | | server.bgrewritechildpid!=-1) {
int statloc;
pid_t pid;
if ((PID = WAIT3 (&statloc,wnohang,null))!= 0) {
if (pid = = server.bgsavechildpid) {
Backgroundsavedonehandler (Statloc);
} else {
Backgroundrewritedonehandler (statloc);
}
Updatedictresizepolicy ();
}
If the child process that caught the write Rdb file exits, the Backgroundsavedonehandler is called for processing
Then look at the Backgroundsavedonehandler function.
rdb.c:1062
void Backgroundsavedonehandler (int statloc) {
int exitcode = Wexitstatus (statloc);
int bysignal = wifsignaled (statloc);
if (!bysignal && ExitCode = = 0) {
redislog (redis_notice, Background saving terminated with
success); C5/>server.dirty = Server.dirty-server.dirty_before_bgsave;
Server.lastsave = time (NULL);
} else if (!bysignal && exitcode!= 0) {
redislog (redis_warning, "Background saving Error");
} else {
R Edislog (redis_warning,
"Background saving terminated by signal%d", Wtermsig (Statloc));
Rdbremovetempfile (server.bgsavechildpid);
}
Server.bgsavechildpid =-1;
/* Possibly there are slaves waiting for a bgsave in order to be served * (the The ' the ' of
SYNC is a stage bulk of Dump.rdb) *
/updateslaveswaitingbgsave (exitcode = 0?) REDIS_OK:REDIS_ERR);
}
Update dirty Data Count Server.dirty 0 and most recently write Rdb file Server.lastsave Wake up for the current time slave waiting for the snapshot to be saved, see slave for specific content on replication
Snapshot Import
When Redis for a power outage or for some reason, when we restart Redis, we need to read the snapshot file from the Rdb file and re-import the data saved to the Rdb file into memory.
Let's take a look at the processing of snapshot import at startup
redis.c:1717
if (server.appendonly) {
if (loadappendonlyfile (server.appendfilename) = = REDIS_OK)
redislog (Redis_notice, " DB loaded from Append only file:%ld seconds ', Time (NULL)-start);
} else {
if (rdbload (server.dbfilename) = = REDIS_OK) {
redislog (redis_notice, "DB loaded from disk:%ld seconds", C5/>time (NULL)-start);
else if (errno!= enoent) {
redislog (redis_warning, "Fatal Error loading the DB. Exiting. ");
Exit (1);
}
}
If you save the AoF file, use the AoF file to restore the data, aof the specific content see AOF If there is no aof, use Rdb file to recover the data, call the Rdbload function
Then look at the Rdbload function.
rdb.c:929
ue fitting
int rdbLoad(char *filename) {
...
fp = fopen(filename,"r");
if (!fp) {
errno = ENOENT;
return REDIS_ERR;
}
if (fread(buf,9,1,fp) == 0) goto eoferr;
buf[9] = "\0";
if (memcmp(buf,"REDIS",5) != 0) {
fclose(fp);
redisLog(REDIS_WARNING,"Wrong signature trying to load DB from file");
errno = EINVAL;
return REDIS_ERR;
}
rdbver = atoi(buf 5);
if (rdbver < 1 || rdbver > 2) {
fclose(fp);
redisLog(REDIS_WARNING,"Can"t handle RDB format version %d",rdbver);
errno = EINVAL;
return REDIS_ERR;
}
startLoading(fp);
while(1) {
robj *key, *val;
int force_swapout;
expiretime = -1;
/* Serve the clients from time to time */
if (!(loops % 1000)) {
loadingProgress(ftello(fp));
aeProcessEvents(server.el, AE_FILE_EVENTS|AE_DONT_WAIT);
}
/* Read type. */
if ((type = rdbLoadType(fp)) == -1) goto eoferr;
if (type == REDIS_EXPIRETIME) {
if ((expiretime = rdbLoadTime(fp)) == -1) goto eoferr;
/* We read the time so we need to read the object type again */
if ((type = rdbLoadType(fp)) == -1) goto eoferr;
}
if (type == REDIS_EOF) break;
/* Handle SELECT DB opcode as a special case */
if (type == REDIS_SELECTDB) {
if ((dbid = rdbLoadLen(fp,NULL)) == REDIS_RDB_LENERR)
goto eoferr;
if (dbid >= (unsigned)server.dbnum) {
redisLog(REDIS_WARNING,"FATAL: Data file was created with a Redis server configured to handle more than %d databases. Exiting\n", server.dbnum);
exit(1);
}
db = server.db dbid;
continue;
}
/* Read key */
if ((key = rdbLoadStringObject(fp)) == NULL) goto eoferr;
/* Read value */
if ((val = rdbLoadObject(type,fp)) == NULL) goto eoferr;
/* Check if the key already expired. This function is used when loading
* an RDB file from disk, either at startup, or when an RDB was
* received from the master. In the latter case, the master is
* responsible for key expiry. If we would expire keys here, the
* snapshot taken by the master may not be reflected on the slave. */
if (server.masterhost == NULL && expiretime != -1 && expiretime < now) {
decrRefCount(key);
decrRefCount(val);
continue;
}
/* Add the new object in the hash table */
dbAdd(db,key,val);
/* Set the expire time if needed */
if (expiretime != -1) setExpire(db,key,expiretime);
/* Handle swapping while loading big datasets when VM is on */
/* If we detecter we are hopeless about fitting something in memory
* we just swap every new key on disk. Directly…
* Note that"s important to check for this condition before resorting
* to random sampling, otherwise we may try to swap already
* swapped keys. */
if (swap_all_values) {
dictEntry *de = dictFind(db->dict,key->ptr);
/* de may be NULL since the key already expired */
if (de) {
vmpointer *vp;
val = dictGetEntryVal(de);
if (val->refcount == 1 &&
(vp = vmSwapObjectBlocking(val)) != NULL)
dictGetEntryVal(de) = vp;
}
decrRefCount(key);
continue;
}
decrRefCount(key);
/* Flush data on disk once 32 MB of additional RAM are used… */
force_swapout = 0;
if ((zmalloc_used_memory() - server.vm_max_memory) > 1024*1024*32)
force_swapout = 1;
/* If we have still some hope of having some value fitting memory
* then we try random sampling. */
if (!swap_all_values && server.vm_enabled && force_swapout) {
while (zmalloc_used_memory() > se
memory * Then we try random sampling. */if (!swap_all_values && server.vm_enabled && force_swapout) {while (zmalloc_used_memory () > SE