1. Description
zset is an ordered set, and the key of zset is non-repeatable.
2. Function
Commonly used for functions such as rankings
3. Use commands
1. Add: zadd
zadd [ redis_key ] [ zset_key ] [ zset_value ] …
example:
zadd player 99 Xiaobai 87 Xiaohong 2.5 Wutiao
2. Delete: zrem
Example: Delete Xiaohong
zrem player 小红
3. Increase scores: zincrby
zincrby [ redis_key ] [ zset_key ] [ zset_value ]
Example: Add Xiaobai’s 99 points + 100 points.
zincrby player 1 小白
4. Subscript range query: zrange
zrange [ redis_key ] [zset start subscript] [zset end subscript] [withscores]
Example: Traverse from the first to the last. Defaults to ascending in zset.
zrange player 0 -1 zrange player 0 -1 withscores
Without withscores, only the values of zset are output.
Add withscores to output the values and keys of zset.
5. Score range query: zrangebyscore
zrangebyscore [ redis_key ] [zset minimum score] [zset maximum score] [withscores]
Example: Query players with scores from 2 to 5. Ascending order.
zrangebyscore player 2 5 zrangebyscore player 2 5 withscores
If the minimum score and the maximum score swap places, descending order can be achieved.
Example: Query players with scores from 2 to 5. Descending order.
zrangebyscore player 5 2
6. Score range statistics: zcount
zcount [ redis_key ] [zset minimum score] [zset maximum score]
Example: Count the total number of people who match the score from 2 to 5
zcount player 2 5
7. Get the subscript: zrank
zrank [ redis_key ] [zset value]
zrank player 五条
You can achieve search ranking through this function.
8. Query the total number of zsets
**zcard [redis_key] **
zcard player
Related documents
4. Underlying data structure – ziplist and skiplist
The bottom layer of zset is implemented by ziplist and skiplist.
When the number of elements stored in zset <= 128 and the size of each element <= 64 bytes, use ziplist, otherwise use skiplist.
1. ziplist skip list structure
struct ziplist<T> {<!-- --> int32 zlbytes;//Memory usage; used when reallocating compressed list memory or calculating the position of zlend. int32 zltail_offset; int16 zllength; //The number of nodes, when it is less than 65535, is the real number; when it is equal to 65535, it must be traversed and calculated. T[] entries; int8 zlend; //0xFF, used to mark the end of the compressed list } struct entry {<!-- --> int<var> prevlen; // The byte length of the previous entry int<var> encoding; // element type encoding optional byte[] content; // element content }
2. skiplist structure
typedef struct zset{<!-- --> //jump table zskiplist *zsl; //Dictionary key saves member, value saves score; dict *dice; } zset; // Jump table typedef struct zskiplist {<!-- --> struct zskiplistNode *header, *tail; unsigned long length; //skip table length int level; //Maximum level of jump table index node } zskiplist; // Jump table node typedef struct zskiplistNode {<!-- --> sds ele; //member member double score; //score struct zskiplistNode *backward; //Previous node pointer struct zskiplistLevel {<!-- --> struct zskiplistNode *forward; //Next node pointer, used to implement multi-layer linked lists. unsigned long span; //Record the distance between two nodes, used to calculate rank ranking } level[]; } zskiplistNode;
Add logic
void zaddGenericCommand(redisClient *c, int incr) {<!-- --> static char *nanerr = "resulting score is not a number (NaN)"; robj *key = c->argv[1]; robj *ele; robj *zobj; robj *curobj; double score = 0, *scores = NULL, curscore = 0.0; int j, elements = (c->argc-2)/2; int added = 0, updated = 0; //The input score - member parameters must appear in pairs if (c->argc % 2) {<!-- --> addReply(c,shared.syntaxerr); return; } // Get all input score values scores = zmalloc(sizeof(double)*elements); for (j = 0; j < elements; j + + ) {<!-- --> if (getDoubleFromObjectOrReply(c,c->argv[2 + j*2], & amp;scores[j],NULL) != REDIS_OK) goto cleanup; } // Get the ordered collection object zobj = lookupKeyWrite(c->db,key); if (zobj == NULL) {<!-- --> //The ordered set does not exist, create a new ordered set if (server.zset_max_ziplist_entries == 0 || server.zset_max_ziplist_value < sdslen(c->argv[3]->ptr)) {<!-- --> zobj = createZsetObject(); } else {<!-- --> zobj = createZsetZiplistObject(); } //Associate objects to the database dbAdd(c->db,key,zobj); } else {<!-- --> //Object exists, check type if (zobj->type != REDIS_ZSET) {<!-- --> addReply(c,shared.wrongtypeerr); goto cleanup; } } // process all elements for (j = 0; j < elements; j + + ) {<!-- --> score = scores[j]; // Sorted collection is ziplist encoded if (zobj->encoding == REDIS_ENCODING_ZIPLIST) {<!-- --> unsigned char *eptr; // Find members ele = c->argv[3 + j*2]; if ((eptr = zzlFind(zobj->ptr,ele, & amp;curscore)) != NULL) {<!-- --> // member already exists // Used when using ZINCRYBY command if (incr) {<!-- --> score + = curscore; if (isnan(score)) {<!-- --> addReplyError(c,nanerr); goto cleanup; } } //When executing the ZINCRYBY command, // Or executed when the user modifies the member's score through ZADD if (score != curscore) {<!-- --> // Delete existing elements zobj->ptr = zzlDelete(zobj->ptr,eptr); // reinsert element zobj->ptr = zzlInsert(zobj->ptr,ele,score); // counter server.dirty + + ; updated + + ; } } else {<!-- --> //The element does not exist, add it directly zobj->ptr = zzlInsert(zobj->ptr,ele,score); // Check the number of elements, // See if you need to convert the ZIPLIST encoding into an ordered set if (zzlLength(zobj->ptr) > server.zset_max_ziplist_entries) zsetConvert(zobj,REDIS_ENCODING_SKIPLIST); // Check the length of the newly added element // See if you need to convert the ZIPLIST encoding into an ordered set if (sdslen(ele->ptr) > server.zset_max_ziplist_value) zsetConvert(zobj,REDIS_ENCODING_SKIPLIST); server.dirty + + ; added + + ; } // Sorted set is SKIPLIST encoded } else if (zobj->encoding == REDIS_ENCODING_SKIPLIST) {<!-- --> zset *zs = zobj->ptr; zskiplistNode *znode; dictEntry *de; // encoding object ele = c->argv[3 + j*2] = tryObjectEncoding(c->argv[3 + j*2]); // Check if the member exists de = dictFind(zs->dict,ele); if (de != NULL) {<!-- --> //member exists //retrieve members curobj = dictGetKey(de); // Get the score curscore = *(double*)dictGetVal(de); // Execute when ZINCRYBY if (incr) {<!-- --> score + = curscore; if (isnan(score)) {<!-- --> addReplyError(c,nanerr); goto cleanup; } } //When executing the ZINCRYBY command, // Or executed when the user modifies the member's score through ZADD if (score != curscore) {<!-- --> // Delete the original element redisAssertWithInfo(c,curobj,zslDelete(zs->zsl,curscore,curobj)); // reinsert element znode = zslInsert(zs->zsl,score,curobj); incrRefCount(curobj); /* Re-inserted in skiplist. */ //Update the score pointer of the dictionary dictGetVal(de) = & amp;znode->score; /* Update score ptr. */ server.dirty + + ; updated + + ; } } else {<!-- --> // If the element does not exist, add it directly to the jump list znode = zslInsert(zs->zsl,score,ele); incrRefCount(ele); /* Inserted in skiplist. */ //Associate the element to the dictionary redisAssertWithInfo(c,NULL,dictAdd(zs->dict,ele, & amp;znode->score) == DICT_OK); incrRefCount(ele); /* Added to dictionary. */ server.dirty + + ; added + + ; } } else {<!-- --> redisPanic("Unknown sorted set encoding"); } } if (incr) /* ZINCRBY */ addReplyDouble(c,score); else /* ZADD */ addReplyLongLong(c,added); cleanup: zfree(scores); if (added || updated) {<!-- --> signalModifiedKey(c->db,key); notifyKeyspaceEvent(REDIS_NOTIFY_ZSET, incr ? "zincr" : "zadd", key, c->db->id); } } //https://blog.csdn.net/qq_27198345/article/details/108674817