DEV Community

Cover image for Redis Sorted Sets Explained
Dunith Danushka
Dunith Danushka

Posted on

Redis Sorted Sets Explained

Before diving in, let's try to understand Sorted Sets by deconstructing the name.

Alt Text

As per the above, Sorted Set is a Set data structure that doesn't allow duplicate members. At the same time, its members are ordered in ascending order. By combining both, we can define a Sorted Set as an ordered collection of unique members.

Sorted Set is similar to the Set data structure in Redis. Members can be a list of non-repeating strings. The only difference is that each member is associated with a score, a floating-point number that provides a sorting order for the Sorted Set. Members are always sorted from the smallest to the greatest score.

The fitness leaderboard

The best way to understand a Sorted Set is to picture it as a table with score and member columns. The following is an imaginary fitness leaderboard where the member column represents the players while the score represents the number of steps they had walked so far.

Alt Text

The leaderboard has been sorted in ascending order, based on the scores. Scores can be duplicated, but the members can not.

If two members have the same score, Redis sorts them based on members' lexicographical order. For example, Jennifer and Peter have the same score of 690. But Redis makes sure to put Jennifer before Peter because J comes before P.

You can also notice the Rank on the right side. Rank is an implicit attribute assigned to each member, based on the magnitude of its score. It's a zero-based index. The member with the smallest score will be assigned the rank of 0.

Rank is useful when you are retrieving members from the sorted set.

Benefits and use cases

Redis guarantees that a Sorted Set is always sorted. There is no additional sorting required for the client who retrieves the stored members back. That saves CPU cycles in the client and also reduces the code complexity.

Sorted Sets allow you to add, remove, or update elements in a speedy way (in a time proportional to the logarithm of the number of elements). Since elements are taken in order and not ordered afterward, you can also get ranges by score or rank (position) quickly. Accessing the middle of a sorted set is also very fast, so you can use Sorted Sets as a smart list of non-repeating elements where you can quickly access everything you need.

That makes Sorted Sets an excellent choice of implementing real-time, low-latency leaderboards, priority queues, and secondary indexes.

So far, we have covered the basics of Sorted Sets. Let's look at some interesting operations we can perform on them.

Operations

We can perform operations on Sorted Sets to add, remove, increment, and retrieve members. Usually, these operations start with the letter 'Z.'

Let's use the fitness leaderboard example we discussed above to demonstrate these operations. Operations are performed using the Redis CLI. Note that I'm not going to cover all available Sorted Set operations here. I would instead glance through the operations that matter when building a leaderboard-like application. For a full list of operations, you can always refer to Sorted Sets documentation.

Add new players and their steps to the game

When a new player joins, he/she should be added to the leaderboard with the number of steps.
The operation ZADD adds one more element to the Sorted Set. Below is the format of the command.

ZADD <key> <score> <member>
Enter fullscreen mode Exit fullscreen mode

key is the name of the Sorted Set. The following command adds Lester with 790 steps to the players. In return, it gives you the number of elements added to the set, which is 1 in this case.

127.0.0.1:6379> ZADD players 790 Lester
(integer) 1
127.0.0.1:6379>
Enter fullscreen mode Exit fullscreen mode

You can add any number of elements along with their scores as follows. Note that the elements are stored according to their score, not based on the order they've added.

127.0.0.1:6379> ZADD players 980 Mary 850 Alice
(integer) 2
127.0.0.1:6379>
Enter fullscreen mode Exit fullscreen mode

After adding new players, the structure of players would look like this.

Alt Text

Increment/decrement player steps

When players perform their daily walks, their step count needs to be updated on the leaderboard.

You can use the ZINCRBY command to increment the score of a given member by any arbitrary value.

ZINCRBY <key> <increment> <member>
Enter fullscreen mode Exit fullscreen mode

The following command increments Tom's steps by 10.

127.0.0.1:6379> ZINCRBY players 10 Tom
"570"
Enter fullscreen mode Exit fullscreen mode

You can use a negative value to decrement a score. The following command decrements Kendra's steps by 5.

127.0.0.1:6379> ZINCRBY players -5 Kendra
"395"
Enter fullscreen mode Exit fullscreen mode

Retrieve top 10 players by score

Now we need to retrieve only the top 10 players to display them on a leaderboard.

ZREVRANGE command makes that possible by returning only a specified set of members based on their reverse order score.
The command takes the following format.

ZREVRANGE <key> <start> <stop> [WITHSCORES]
Enter fullscreen mode Exit fullscreen mode

ZREVRANGE returns members in reversed-rank order, with scores ordered from high to low. You have to specify starting and ending rank index positions with start and stop parameters.

Let's get the top 10 players by the steps they had walked.

127.0.0.1:6379> ZREVRANGE players 0 9
1) "Mary"
2) "Alice"
3) "Lester"
4) "Frank"
5) "Peter"
6) "Jennifer"
7) "Barbara"
8) "Tom"
9) "Kendra"
10) "Dave"
Enter fullscreen mode Exit fullscreen mode

There is one additional parameter: WITHSCORES, which returns the score for each member.

127.0.0.1:6379> ZREVRANGE players 0 9 WITHSCORES
1) "Mary"
2) "980"
3) "Alice"
4) "850"
5) "Lester"
6) "790"
7) "Frank"
8) "740"
9) "Peter"
10) "690"
11) "Jennifer"
12) "690"
13) "Barbara"
14) "650"
15) "Tom"
16) "570"
17) "Kendra"
18) "395"
19) "Dave"
20) "340"
Enter fullscreen mode Exit fullscreen mode

The ZRANGE command does the opposite to this. It returns members in rank order, with scores ordered from low to high.

The following command returns the first ten players who had walked the least number of steps.

127.0.0.1:6379> ZRANGE players 0 9
1) "Dave"
2) "Kendra"
3) "Tom"
4) "Barbara"
5) "Jennifer"
6) "Peter"
7) "Frank"
8) "Lester"
9) "Alice"
10) "Mary"
Enter fullscreen mode Exit fullscreen mode

Retrieve the rank and the score of an individual player

Now we need to find the score and the rank of a given player. Sometimes, that can be displayed on the player's profile.

ZRANK and ZREVRANK commands return the rank of a member, which is a 0 based index position.

The format of the command is:

[ZRANK|ZREVRANK] <key> <member>
Enter fullscreen mode Exit fullscreen mode

The following command returns 0 for Dave, who's currently at the bottom of the leaderboard.

127.0.0.1:6379> ZRANK players Dave
(integer) 0
Enter fullscreen mode Exit fullscreen mode

Similarly, ZREVRANK for Mary should return 0. Because it returns the rank in the reverse order, with scores sorted from low to high.

127.0.0.1:6379> ZREVRANK players Mary
(integer) 0
Enter fullscreen mode Exit fullscreen mode

ZSCORE provides the current score associated with the member. The format of the command is:

ZSCORE <key> <member>
Enter fullscreen mode Exit fullscreen mode

This returns Peter's score, which is 690.

127.0.0.1:6379> ZSCORE players Peter
"690"
Enter fullscreen mode Exit fullscreen mode

Get the player count based on the score

How can we find the number of players who had walked between a given range of steps? For example, how many players had walked more than 500 but less than 700 steps?

The command ZCOUNT will count the members between the min and the max score. That is inclusive. The format of the command is:

ZCOUNT <key> <min> <max>
Enter fullscreen mode Exit fullscreen mode

The following command returns the number of players who had walked between 500 and 700 steps.

127.0.0.1:6379> ZCOUNT players 500 700
(integer) 4
Enter fullscreen mode Exit fullscreen mode

Remove players from the game

When players leave the game, we need to remove them from the leaderboard.

The ZREM command removes one or more members from a Sorted Set. The command format is:

ZREM <key> <member> [<member> …]
Enter fullscreen mode Exit fullscreen mode

We can remove Barbara from the leaderboard as she decided to leave the game because she was tired.

127.0.0.1:6379> ZREM players Barbara
(integer) 1
Enter fullscreen mode Exit fullscreen mode

Finally, after all these operations, our leaderboard looks like this.

Alt Text

Conclusion

Due to its natural ordering and efficiency in accessing stored elements, Sorted Sets are getting increasingly popular for building applications like real-time leaderboards. They typically store more than a million elements in memory and provide low-latency access to real-time dashboards.

Implementing the same functionality with a traditional database would always be challenging. Even if you manage to do that, feeding that to a dashboard will be a recipe for disaster.

Thus, an in-memory data structure such as Redis shines well here.

References

https://redis.io/topics/data-types

Top comments (1)

Collapse
 
abhishe01794149 profile image
Abhishek Chandel

well written & explained