After the last zipmap analysis, the content about the redis source code struct has actually ended, because there are several operation classes related to the struct below, they are merged into the struct package. These files include t_hash.c, z_list, z_set.c, t_string.c, and t_zset.c. The functions of these files are similar.
After the last zipmap analysis, the content about the redis source code struct has actually ended, because there are several operation classes related to the struct below, they are merged into the struct package. These files include t_hash.c, z_list, z_set.c, t_string.c, and t_zset.c. The functions of these files are similar.
After the last zipmap analysis, the content about the redis source code struct has actually ended, because there are several operation classes related to the struct below, they are merged into the struct package. These types of files include t_hash.c, z_list, z_set.c, t_string.c, and t_zset.c. The functions of these files are similar, that is, the operation class used to implement command processing between the Client and Server, store dict and ziplist into robj in the form of robj, and perform various conversions to implement command operations. Avoiding the complex structure of the struct, it encapsulates the operation class of the struct. Today I am talking about t_hash, which is the dict hash dictionary, and the conversion between ziplist compression list and robj. HashType. Because this file has no header file and only the. c file, I pulled out the method for convenience of learning.
/* The following is the classification of Methods */
Void hashTypeTryConversion (robj * o, robj ** argv, int start, int end)/* When hashType is ziplist, determine whether the object length exceeds the maximum ziplist length accepted by the server, if the value is exceeded, it is converted to the hash dictionary type */
Void hashTypeTryObjectEncoding (robj * subject, robj ** o1, robj ** o2)/* When robj uses a dictionary encoding method, the code is converted */
Int hashTypeGetFromZiplist (robj * o, robj * field, unsigned char ** vstr, unsigned int * vlen, long * vll) /* obtain the value at an index position in the ziplist compression list */
Int hashTypeGetFromHashTable (robj * o, robj * field, robj ** value)/* Get a value in the hash dictionary */
Robj * hashTypeGetObject (robj * o, robj * field)/* Get the object type corresponding to a key */
Int hashTypeExists (robj * o, robj * field)/* hastType determines whether a key exists */
Int hashTypeSet (robj * o, robj * field, robj * value)/* hashType setting operation, in two cases: ziplist, and dictionary hashtable */
Int hashTypeDelete (robj * o, robj * field)/* hashType delete operation, which can be divided into ziplist delete operation and hashtable delete operation */
Unsigned long hashTypeLength (robj * o)/* hashType length operation */
HashTypeIterator * hashTypeInitIterator (robj * subject)/* Get hashType iterator */
Void hashTypeReleaseIterator (hashTypeIterator * hi)/* release the hashType iterator */
Int hashTypeNext (hashTypeIterator * hi)/* Get the next element through the hashType iterator */
Void hashTypeCurrentFromZiplist (hashTypeIterator * hi, int what, unsigned char * vstr, unsigned int * vlen, long * vll)/* based on the position of the current iterator, obtains the key location of the current ziplist or the value of the key location */
Void hashTypeCurrentFromHashTable (hashTypeIterator * hi, int what, robj ** dst)/* obtains the key position of the current dict location based on the current iterator location, or the value at this position */
Robj * hashTypeCurrentObject (hashTypeIterator * hi, int what)/* Get the current key object based on the position of the current iterator */
Robj * hashTypeLookupWriteOrCreate (redisClient * c, robj * key)/* locate whether the key exists based on the c client object and create or add the key */
Void hashTypeConvertZiplist (robj * o, int enc)/* convert from ziplist to hashtable */
Void hashTypeConvert (robj * o, int enc)/* object conversion operations, such as conversion from ziplist to dict */
The relevant Operation Command class of hashType is actually a combined call to the above method:
/* Hash command type */void hsetCommand (redisClient * c)/* client setting command */void hsetnxCommand (redisClient * c) /* set the next location command on the client */void hmsetCommand (redisClient * c)/* set the command on the client ticket. If no key exists, perform subsequent operations */void hincrbyCommand (redisClient * c) /* add value operations on the client */void hincrbyfloatCommand (redisClient * c)/* Add float Type value operations on the client */static void addHashFieldToReply (redisClient * c, robj * o, robj * field)/**/void hgetCommand (redisClient * c)/* client acquisition operation. If no operation is found, no operation is performed directly */void hmetcommand (redisClient * c) /* The client obtains the key operation. If it is NULL, some NULL values */void hdelCommand (redisClient * c)/* client deletion operation */void hlenCommand (redisClient * c) will be returned) /* client length command */static void addHashIteratorCursorToReply (redisClient * c, hashTypeIterator * hi, int what)/* Add hashType iterator operation on the client */void genericHgetallCommand (redisClient * c, int flags)/* original method for obtaining the client operation. You can add the flag parameter */void hkeysCommand (redisClient * c)/* command for obtaining the key value from the client */void hvalsCommand (redisClient * c) /* command for obtaining the val value from the client */void hgetallCommand (redisClient * c)/* command for obtaining the key from the client; get both values */void hexistsCommand (redisClient * c) /* check whether the operation exists on the client */void hscanCommand (redisClient * c)/* scan the client */
Robj's operation is very simple. rob uses the ptr pointer in it to store the real ziplist or dict hash class, and the subsequent operations are based on this, for example, the following method:
/* Get the value from a hash table encoded hash, identified by field. * Returns-1 when the field cannot be found. * // * obtain a value in the hash dictionary */int hashTypeGetFromHashTable (robj * o, robj * field, robj ** value) {dictEntry * de; redisAssert (o-> encoding = REDIS_ENCODING_HT); // use the dict class stored in robj-> ptr or ziplist class to start searching for de = dictFind (o-> ptr, field ); if (de = NULL) return-1; // obtain the value * value = dictGetVal (de); return 0 ;}
All struct operations related to robj will be processed in two cases. ZIPLIST and HASH are dict types, and transcoding is required when ziplist operations are performed, of course, the ziplist should be encoded when it is stored in robj. It can be seen that the designer still thinks well when considering the command transmission, and also considers the security issue.
/* Add an element, discard the old if the key already exists. * Return 0 on insert and 1 on update. * This function will take care of incrementing the reference count of the * retained fields and value objects. * // * hashType setting operation, which can be divided into ziplist and dictionary hashtable */int hashTypeSet (robj * o, robj * field, robj * value) {int update = 0; if (o-> encoding = REDIS_ENCODING_ZIPLIST) {unsigned char * zl, * fptr, * vp Tr; // first decode the field and value field = getDecodedObject (field); value = getDecodedObject (value); zl = o-> ptr; fptr = ziplistIndex (zl, ZIPLIST_HEAD); if (fptr! = NULL) {fptr = ziplistFind (fptr, field-> ptr, sdslen (field-> ptr), 1); if (fptr! = NULL) {/* Grab pointer to the value (fptr points to the field) */vptr = ziplistNext (zl, fptr); redisAssert (vptr! = NULL); update = 1; // you can Delete the operation before inserting a new value/* Delete value */zl = ziplistDelete (zl, & vptr ); /* Insert new value */zl = ziplistInsert (zl, vptr, value-> ptr, sdslen (value-> ptr) ;}} if (! Update) {/* Push new field/value pair onto the tail of the ziplist */zl = ziplistPush (zl, field-> ptr, sdslen (field-> ptr ), ZIPLIST_TAIL); zl = ziplistPush (zl, value-> ptr, sdslen (value-> ptr), ZIPLIST_TAIL);} o-> ptr = zl; // after it is used up, decrRefCount (field); decrRefCount (value);/* Check if the ziplist needs to be converted to a hash table */if (hashTypeLength (o)> server. (Optional) hashTypeConvert (o, REDIS_ENCODING_HT);} else if (o-> encoding = REDIS_ENCODING_HT) {// replace if (dictReplace (o-> ptr, field, value) {/* Insert */incrRefCount (field);} After else {/* Update */update = 1, decrease reference count incrRefCount (value);} else {redisPanic ("Unknown hash encoding");} return update ;}
In this process, the redis Code also uses a reference counting item, which should be used for reasonable memory release control and can be seen in many places;
/* Higher level function of hashTypeGet * () that always returns a Redis * object (either new or with refcount incremented ), so that the caller * can retain a reference or call decrRefCount after the usage. ** The lower level function can prevent copy on write so it is * the preferred way of doing read operations. * // * Get the object of a key */robj * hashTypeGetObject (robj * o, robj * field) {robj * value = NULL; if (o-> encoding = REDIS_ENCODING_ZIPLIST) {unsigned char * vstr = NULL; unsigned int vlen = UINT_MAX; long vll = LLONG_MAX; if (hashTypeGetFromZiplist (o, field, & vstr, & vlen, & vll) = 0) {// obtain the value if (vstr) {value = createStringObject (char *) vstr, vlen) in ziplist );} else {value = createStringObjectFromLongLong (vll) ;}} else if (o-> encoding = REDIS_ENCODING_HT) {robj * aux; if (hashTypeGetFromHashTable (o, field, & aux) = 0) {// The object is referenced, and the count increases by incrRefCount (aux); value = aux ;}} else {redisPanic ("Unknown hash encoding ");} return value ;}
The client's command operation is actually based on an object called redisClient, which is actually a robj object. During Command Transmission, this robj-> ptr is stored, and the specific data, robj-> args [] stores various parameters, followed by calling the previous method. The only difference is that there must be a reply and update notification operation after the command is called ., The following is a set command;
/* HashType processes Client Command requests */void hsetCommand (redisClient * c) {int update; robj * o; if (o = hashTypeLookupWriteOrCreate (c, c-> argv [1]) = NULL) return; hashTypeTryConversion (o, c-> argv, 2, 3); hashTypeTryObjectEncoding (o, & c-> argv [2], & c-> argv [3]); // The operation of the command is through, the object in the client, and the command parameters in it constitute update = hashTypeSet (o, c-> argv [2], c-> argv [3]); // after the operation, add the reply addReply (c, update? Shared. czero: shared. cone); // sending a notification indicates that the command has been executed. The prediction will trigger the display of signalModifiedKey (c-> db, c-> argv [1]) in the window; policykeyspaceevent (redis_policy_hash, "hset", c-> argv [1], c-> db-> id); // The Client Command is successfully executed, because the data at this time is the latest, the dirty data on the server is naturally one more, server. dirty ++ ;}
Other commands are similar. As you can see, the code at the logic layer is now slightly closer, and the code at the end must be wonderful.