Redis Data Type - Collection Object, Ordered Collection Object

Collection object

set types are also used to hold multiple string elements
Unlike list types, duplicate elements are not allowed in a collection, and elements in a collection are out of order and cannot be retrieved through an index subscript

A collection can store up to 232-1 elements
Redis not only supports addition, deletion, and censoring within a set, but also supports intersection, Union and difference of multiple sets, and makes good use of set types.

The encoding of a collection object can be intset or hashtable
Reading Reference: Redis Data Structure (5) - Integer Set
The intset-encoded collection object uses an integer collection as the underlying implementation, and all elements contained in the collection object are stored in the integer collection

For example, the following code creates an intset-encoded collection object

redis> SADD numbers 1 3 5
(integer) 3

On the other hand, hashtable-encoded collection objects use a dictionary as the underlying implementation, where each key is a string object, each string object contains a collection element, and the values of the dictionary are all set to NULL

For example, the following code creates a hashtable-encoded collection object

Encoding Conversion of Collection Objects

When a collection object can satisfy both of the following conditions, it uses intset encoding

  • All elements saved by a collection object are integer values
  • Collection objects hold no more than 512 elements

Collection objects that do not meet these two conditions need hashtable encoding

Be careful:
The upper limit of the second condition can be modified, see the description of the set-max-intset-entries option in the configuration file
For collection objects that use intset encoding, when either of the two conditions required for intset encoding cannot be met, the object's encoding conversion operation is performed, all elements that were originally stored in the integer collection are transferred and saved in the dictionary, and the object's encoding changes from intset to hashtable

For example, the following code creates a collection object that contains only integer elements and is coded as intset

redis> SADD numbers 1 3 5
(integer) 3
redis> OBJECT ENCODING numbers
"intset"

However, as long as a string element is added to this collection object that contains only integer elements, the encoding transfer of the collection object will be performed

redis> SADD numbers "seven"
integer) 1
redis> OBJECT ENCODING numbers
"hashtable"

In addition, if you create a collection object with 512 integer elements, the object's encoding should be intset

redis> EVAL "for i=1, 512 do redis.call('SADD', KEYS[1], i) end" 1 integers
(nil)
redis> SCARD integers
(integer) 512
redis> OBJECT ENCODING integers
"intset"

However, as long as a new integer element is added to the set, making the number of elements in the set 513, the object's encoding conversion operation will be performed

redis> SADD integers 10086
(integer) 1
redis> SCARD integers
(integer) 513
redis> OBJECT ENCODING integers
"hashtable"

Commands for Collection Objects



Operation within collection

1. Add Element-sadd

sadd key element [element ...]

Returns the number of elements successfully added, such as

127.0.0.1:6379> exists myset
(integer) 0
127.0.0.1:6379> sadd myset a b c
(integer) 3
127.0.0.1:6379> sadd myset a b
(integer) 0

2. Delete element-srem

srem key element [element ...]

Returns the number of elements successfully deleted, for example

127.0.0.1:6379> srem myset a b
(integer) 2
127.0.0.1:6379> srem myset hello
(integer) 0

3. Calculate Number of Elements - scard

scard key

The time complexity of scard is O(1), which does not traverse all elements of the collection, but uses variables directly inside Redis

127.0.0.1:6379> scard myset
(integer) 1

4. Determine if an element is in the set-sismember

sismember key element

If a given element element element element returns 1 within a collection and vice versa returns 0, for example

127.0.0.1:6379> sismember myset c
(integer) 1

5. Randomly returns a specified number of elements from a set - srandmember

srandmember key [count]

[count] is an optional parameter if it is not written to default to 1, for example

127.0.0.1:6379> srandmember myset 2
1) "a"
2) "c"
127.0.0.1:6379> srandmember myset
"d"

6. Randomly eject element-spop from set

spop key

A spop operation can randomly pop up an element from a collection, for example, the following code is a spop and the collection element becomes "d b a"

127.0.0.1:6379> spop myset
"c"
127.0.0.1:6379> smembers myset
1) "d"
2) "b"
3) "a" 

It is important to note that Redis started at version 3.2 and spop also supports the [count] parameter
Both srandmember and spop randomly select elements from a set, unlike srandmember, which deletes elements from the set after the spop command executes

7. Get all elements - smembers

smembers key

The following code takes all elements of the collection myset and returns the result out of order

127.0.0.1:6379> smembers myset
1) "d"
2) "b"
3) "a"

smembers and lrange, hgetall are heavier commands, which can be accomplished using sscan if there is a possibility of blocking Redis with too many elements

Inter-Collection Operations

There are now two collections, user:1:follow and user:2:follow.

127.0.0.1:6379> sadd user:1:follow it music his sports
(integer) 4
127.0.0.1:6379> sadd user:2:follow it news ent sports
(integer) 4

1. Find the intersection of multiple sets-sinter

sinter key [key ...]

For example, the following code finds the intersection of two sets of user:1:follow and user:2:follow and returns the result sports, it

127.0.0.1:6379> sinter user:1:follow user:2:follow
1) "sports"
2) "it"

2. Seek the union of multiple sets-suinon

suinon key [key ...]

For example, the following code finds the union of two sets of user:1:follow and user:2:follow and returns the result sports, it, his, news, music, ent

127.0.0.1:6379> sunion user:1:follow user:2:follow
1) "sports"
2) "it"
3) "his"
4) "news"
5) "music"
6) "ent"

3. Find the difference of multiple sets-sdiff

sdiff key [key ...]

For example, the following code finds the difference between two sets of user:1:follow and user:2:follow and returns music and his

127.0.0.1:6379> sdiff user:1:follow user:2:follow
1) "music"
2) "his"

4. Save the results of intersection, Union and difference sets

sinterstore destination key [key ...]
suionstore  destination key [key ...]
sdiffstore  destination key [key ...]

Operations between sets can be time consuming when there are many elements, so Redis provides the three commands above (the original command + store) to save the results of intersections, unions, and differences between sets in destination key
For example, the following saves the result of the intersection of two sets of user:1:follow and user:2:follow in user:1_2:inter, and user:1_2:inter itself is a collection type

127.0.0.1:6379> sinterstore user:1_2:inter user:1:follow user:2:follow
(integer) 2
127.0.0.1:6379> type user:1_2:inter
set
127.0.0.1:6379> smembers user:1_2:inter
1) "it"
2) "sports"

Scenarios for collecting objects (Important!!!)

Typical usage scenarios for collection types are tag s
For example, one user may be more interested in entertainment, sports, and another in history, news. These are the tags.
With this data, you can get tags that people who like the same tag and users'common preferences. This data is important for the user experience and for enhancing user stickiness.

For example, an e-commerce website will make different types of recommendations for users with different labels, such as people who are interested in digital products, recommending the latest digital products to them on various pages or by email, which usually brings more benefits to the website.

Here are some of the tag functions implemented using collection types
(1) Label users

sadd user:1:tags tag1 tag2 tag5
sadd user:2:tags tag2 tag3 tag5
 ...
sadd user:k:tags tag1 tag2 tag4
...

(2) Add users to labels

sadd tag1:users user:1 user:3
sadd tag2:users user:1 user:2 user:3
...
sadd tagk:users user:1 user:2
...

(3) Delete labels under users

srem user:1:tags tag1 tag5
...

(4) Delete users under Tags

srem tag1:users user:1
srem tag5:users user:1
...

(3) Calculate tags of common interest to users
You can use the sinter command to calculate tags of common interest to users, as shown in the code below

sinter user:1:tags user:2:tags

summary

Collection types are typically used in the following scenarios

  • sadd=Tagging (label)
  • spop/srandmember=Random item
  • sadd+sinter=Social Graph

Ordered Collection Object

Ordered collections are a little unfamiliar to hashes, lists, and collections, but since they are called ordered collections, they must be related to collections. They retain the feature that collections cannot have duplicate members, but the elements in ordered collections can be ordered.
But unlike lists that use index subscripts as their sorting criteria, it sets a score for each element to be sorted by
Elements in an ordered set cannot be duplicated, but score can

Similarities and differences among list, set, ordered set

The encoding of an ordered set can be ziplist or skiplist
Reading Reference: Redis Data Structure (6) - Compressed List ziplist

ziplist-encoded compressed list objects use a compressed list as the underlying implementation, where each collection element is saved with two compact list nodes next to each other, the first node holds the member of the element, and the second element holds the score of the element.

Collection elements in a compressed list are sorted from smallest to largest, with smaller elements placed near the header and larger elements placed near the tail of the table

For example, if the following ZADD command is executed, the server will create an ordered collection object as the value of the price key

redis> ZADD price 8.5 apple 5.0 banana 6.0 cherry
(integer) 3



Skplist-encoded ordered collection objects use the zset structure as the underlying implementation, a zset structure containing a dictionary and a jump table

typedef struct zset {
    zskiplist *zsl;
    dict *dict;
} zset;

The zsl jump table in the zset structure saves all set elements by score from smallest to largest, and each jump table node saves one set element
The object attribute of a skip table node holds the members of an element
The score attribute of a skip table node holds the element's score
With this jump table, programs can perform range operations on ordered collections, such as ZRANK, ZRANGE and other commands, which are based on the jump table API.

In addition, dict dictionaries in the zset structure create a mapping from members to scores for ordered collections
Each key-value pair in a dictionary holds a collection element, the key of the dictionary holds the members of the element, and the value of the dictionary holds the score of the element.

Using this dictionary, programs can find the score of a given member with O(1) complexity, and the ZSCORE command is based on this feature, which is used internally by many other ordered set commands.

Ordered set Each element's member is a string object, and each element's score is a floating point number of type double
Although the zset structure uses both a skip table and a dictionary to hold ordered collection elements, both data structures share members and values of the same element through pointers, so using both a skip table and a dictionary to hold collection elements does not result in any duplicate members or scores, nor does it waste additional memory

For example, if the preceding price key creates an ordered set object that is not ziplist-coded but skiplist-coded, then the ordered set object and object use the zset structure shown below.

Why do ordered collections need to be implemented using both skip tables and dictionaries?

In theory, ordered collections can be implemented using either a dictionary or one of the data structures of a jump table alone, but using either a dictionary or a jump table alone can reduce performance compared to using both a dictionary and a jump table
For example, if only dictionaries are used to implement ordered collections, then O(1) is used instead.Complexity Find Members'Scoring is preserved, but because dictionaries store collection elements in an out-of-order manner, the program needs to sort all the elements saved by the dictionary each time it performs a range operation, such as ZRANK, ZRANGE, and so on. This sort requires at least O(NlogN) time complexity and additional O(N) memory space.(because you want to create an array to hold the sorted elements)

On the other hand, if only a skip table is used to implement an ordered set, all the advantages of a skip table performing a range operation are preserved, but because there is no dictionary, the complexity of finding scores based on members increases from O(1) to O(logN)

For these reasons, Redis chose to use both dictionary and skip table data structures to implement ordered collections in order to make both the lookup of ordered collections and the scoped operation as fast as possible.

Coding Conversion of Ordered Collection Objects

An ordered collection object uses ziplist encoding when it satisfies both of the following conditions

  • Ordered collections hold fewer than 128 elements
  • All element members stored in an ordered collection are less than 64 bytes long

Ordered collection objects that do not meet these two conditions will use skiplist encoding

The upper limit of the above two conditions can be modified. See the description of the zset-max-ziplist-entries option and the zset-max-ziplist-value option in the configuration file

For ordered collection objects using ziplist encoding, when either of the two conditions required for ziplist encoding cannot be met, the object's encoding conversion operation is performed. All collection elements originally stored in the compressed list are transferred and saved in the zset structure, and the object's encoding changes from ziplist to skiplist.

The following code shows how an ordered collection object can cause encoding conversions because it contains too many elements

#
Object contains 128
 Elements
redis> EVAL "for i=1, 128 do redis.call('ZADD', KEYS[1], i, i) end" 1 numbers
(nil)
redis> ZCARD numbers
(integer) 128
redis> OBJECT ENCODING numbers
"ziplist"

# 
Add a new element
redis> ZADD numbers 3.14 pi
(integer) 1

#
Object contains 129 elements
 individual
redis> ZCARD numbers
(integer) 129

# 
Encoding changed
redis> OBJECT ENCODING numbers
"skiplist"

The following code shows an ordered collection object that causes encoding conversions because its members are too long

# 
Add an element to an ordered collection that has only three bytes of members
redis> ZADD blah 1.0 www
(integer) 1
redis> OBJECT ENCODING blah
"ziplist"

#
Add a member to an ordered collection of 66
 Elements of byte length
redis> ZADD blah 2.0 ooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo
(integer) 1

# 
Encoding changed
redis> OBJECT ENCODING blah
"skiplist"

Commands for Ordered Collection Objects (to be perfected)


Within a collection

1. Add Member-zadd

zadd key score member [score member ...]

Add user tom and his score 251 to the ordered collection user:ranking

127.0.0.1:6379> zadd user:ranking 251 tom 
(integer) 1

Return results represent the number of successfully added members

127.0.0.1:6379> zadd user:ranking 1 kris 91 mike 200 frank 220 tim 250 martin
(integer) 5

There are two things to note about the zadd command:

  • Redis3.2 adds four options for the zadd command: nx, xx, ch, incr:
    1. nx:member must not exist before it can be set successfully for addition
    2. xx:member must exist before it can be set up successfully for updating
    3. ch: The number of ordered collection elements and scores that have changed since the operation was returned
    4. incr: add score, equivalent to zincrby described later.
  • An ordered set provides a sort field over a set, but at a cost, zadd has O(log(n)) time complexity and sadd has O(1)

2. Calculate the number of members - zcard

zcard key

For example, the following operation returns an ordered set user:ranking with a membership of 5, and as with scard commands of the set type, zcard has an O(1) time complexity

127.0.0.1:6379> zcard user:ranking
(integer) 5

3. Calculate a member's score-zscore

zscore key member

tom has a score of 251 and returns nil if the member does not exist

127.0.0.1:6379> zscore user:ranking tom
"251"
127.0.0.1:6379> zscore user:ranking test
(nil)

4. Calculate the rank of members - zrank, zrevrank

zrank key member
zrevrank key member

zrank returns rank from low to high, whereas zrevrank returns
For example, in the following operations, tom ranks 5th and 0th in zrank and zrevrank, respectively (ranking starts at 0).

127.0.0.1:6379> zrank user:ranking tom
(integer) 5
127.0.0.1:6379> zrevrank user:ranking tom
(integer) 0

5. Delete member-zrem

zrem key member [member ...]

The following action removes the member mike from the ordered collection user:ranking

127.0.0.1:6379> zrem user:ranking mike
(integer) 1

Number of successful deletions returned

6. Increase Membership Score-zincrby

zincrby key increment member

The following adds 9 points to tom and changes the score to 260 points

127.0.0.1:6379> zincrby user:ranking 9 tom
"260"

7. Return members of the specified ranking range - zrange, zrevrange

zrange    key start end [withscores]
zrevrange key start end [withscores]

Ordered sets are ranked by score, zrange returns from low to high, and zrevrange returns
The following code returns the lowest ranked three members, with the withscores option added, returning the member's score

127.0.0.1:6379> zrange user:ranking 0 2 withscores
1) "kris"
2) "1"
3) "frank"
4) "200"
5) "tim"
6) "220"
127.0.0.1:6379> zrevrange user:ranking 0 2 withscores
1) "tom"
2) "260"
3) "martin"
4) "250"
5) "tim"
6) "220"

8. Returns members of the specified score range - zrangebyscore, zrevrangebyscore

zrangebyscore    key min max [withscores] [limit offset count]
zrevrangebyscore key max min [withscores] [limit offset count]

Where zrangebyscore returns from low to high, whereas zrevrangebyscore returns
For example, if the following operation returns members with a score of 200 to 221 from low to high, the withscores option returns each member's score at the same time
The [limit offset count] option limits the starting location and number of outputs

127.0.0.1:6379> zrangebyscore user:ranking 200 tinf withscores
1) "frank"
2) "200"
3) "tim"
4) "220"
127.0.0.1:6379> zrevrangebyscore user:ranking 221 200 withscores
1) "tim"
2) "220"
3) "frank"
4) "200"

min and max also support open (parentheses) and closed (middle parentheses), where -inf and + inf represent infinite small and infinite large, respectively.

127.0.0.1:6379> zrangebyscore user:ranking (200 +inf withscores
1) "tim"
2) "220"
3) "martin"
4) "250"
5) "tom"
6) "260"

9. Returns the number of members in the specified score range - zcount

zcount key min max

The following returns the number of members with a score of 200 to 221

127.0.0.1:6379> zcount user:ranking 200 221
(integer) 2

10. Delete the ascending element in the specified rank - zremrangebyrank

zremrangebyrank key start end

The following action deletes the members from start to end

127.0.0.1:6379> zremrangebyrank user:ranking 0 2
(integer) 3

11. Delete members of the specified score range - zremrangebyscore

zremrangebyscore key min max

The following action deletes all members with more than 250 points and returns the number of successful deletions

127.0.0.1:6379> zremrangebyscore user:ranking (250 +inf
(integer) 2

Operations between collections

Suppose the following two ordered sets are imported into Redis
user:ranking:1 and user:ranking:2

127.0.0.1:6379> zadd user:ranking:1 1 kris 91 mike 200 frank 220 tim 250 martin 
    251 tom
(integer) 6
127.0.0.1:6379> zadd user:ranking:2 8 james 77 mike 625 martin 888 tom
(integer) 4

1. Intersection-zinterstore

zinterstore destination numkeys key [key ...] [weights weight [weight ...]] 
  [aggregate sum|min|max]

This command has more parameters

  • destination, the intersection result is saved to this key
  • numkeys, the number of computed keys that need to be intersected
  • key[key...], the key that needs to be intersected
  • weights weight[weight...], the weight of each key, each member in each key multiplies its score by this weight when doing intersection calculations, and the weight of each key defaults to 1
  • aggregate sum|min|max, after calculating member intersection, the score can be summarized by sum (sum), min (minimum), Max (maximum), default value is sum

The following intersects user:ranking:1 and user:ranking:2, weights and aggregate s use the default configuration, and you can see that the target key user:anking:1_inter_2 sum s the value

127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:ranking:1 
    user:ranking:2
(integer) 3
127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores
1) "mike"
2) "168"
3) "martin"
4) "875"
5) "tom"
6) "1139"

If you want the weight of user:ranking:2 to be 0.5 and the aggregation effect to use max, you can do the following

127.0.0.1:6379> zinterstore user:ranking:1_inter_2 2 user:ranking:1 
  user:ranking:2 weights 1 0.5 aggregate max
integer) 3
127.0.0.1:6379> zrange user:ranking:1_inter_2 0 -1 withscores
1) "mike"
2) "91"
3) "martin"
4) "312.5"
5) "tom"
6) "444"

2. Union-zunionstore

zunionstore destination numkeys key [key ...] [weights weight [weight ...]] 
  [aggregate sum|min|max]

All the parameters of the command are identical to the zinterstore, but only a union calculation is done, such as calculating the union of user:ranking:1 and user:ranking:2, weights and aggregate using the default configuration, and you can see that the target key user:ranking:1_union_2 sum s the value

127.0.0.1:6379> zunionstore user:ranking:1_union_2 2 user:ranking:1 
    user:ranking:2
(integer) 7
127.0.0.1:6379> zrange user:ranking:1_union_2 0 -1 withscores
 1) "kris"
 2) "1"
 3) "james"
 4) "8"
 5) "mike"
 6) "168"
 7) "frank"
 8) "200"
 9) "tim"
10) "220"
11) "martin"
12) "875"
13) "tom"
14) "1139"

Scenarios for ordered collection of objects (Important!!!)

Ordered collections are typically used in a leaderboard system
Video websites, for example, need to rank videos uploaded by users. The dimensions of the rankings can be in many ways: by time, by number of playback, by number of favorites received.
The following uses the favorite dimension to record the rankings of daily video uploads by users. There are four main functions that need to be implemented
(1) Add user favorites
For example, user mike uploaded a video and got three reviews for using zadd and zincrby functions in an ordered collection

zadd user:ranking:2016_03_15 mike 3

If you get another favor later, you can use zincrby

zincrby user:ranking:2016_03_15 mike 1

(2) Cancel user approval
For various reasons (such as user logoff, user cheating) you need to delete the user, at which point you need to delete the user from the list, you can use zrem. For example, delete member tom

zrem user:ranking:2016_03_15 mike

(3) Display the ten most popular users
This function is implemented using the zrevrange command

zrevrangebyrank user:ranking:2016_03_15 0 9 

(4) Display user information and user scores
This function uses the user name as a key suffix and saves the user information in a hash type. You can use zscore and zrank for score and rank

hgetall user:info:tom
zscore user:ranking:2016_03_15 mike
zrank user:ranking:2016_03_15 mike

Keywords: Redis set

Added by azn_romeo_4u on Wed, 13 Oct 2021 19:38:48 +0300