Redis®*: Rank users in real time
Learn how to rank 2 million users by score in real time using Redis
👋 Welcome to the Stackhero documentation!
Stackhero offers a ready-to-use Redis cloud solution that provides a host of benefits, including:
Redis Commanderweb UI included.- Unlimited message size and transfers.
- Effortless updates with just a click.
- Optimal performance and robust security powered by a private and dedicated VM.
Save time and simplify your life: it only takes 5 minutes to try Stackhero's Redis cloud hosting solution!
One of our clients reached out with an interesting challenge. He runs a sports website where users earn points when they win bets or complete various actions.
His goal was to display each user's rank, show the users immediately above and below them, and generate a top-100 leaderboard. With a community of 2 million users, the data needed to be processed in real time!
At Stackhero, we love tackling challenges like this. In this article, we will guide you through our solution step by step.
Why we chose Redis
Traditional databases such as MySQL, PostgreSQL, or Elasticsearch are not designed for low-latency ranking tasks. This prompted us to explore another option.
We selected Redis, an in-memory database that is extremely fast and reliable. According to DB-Engines, it is the 7th most used database in the world and the leading option in the "Key-value store" category.
Redis offers multiple data models. For this scenario, one stands out: the "sorted sets."
Technical validations
Sorted sets combine a key and a score. In our case, the key is the user ID and the score represents the user's points.
We began by launching a Redis service on Stackhero. The service is up in just 2 minutes with the latest stable release, offers hourly billing, and features Redis Commander, a handy web GUI. We validated the concept using this interface.
We added three users with sample IDs and scores as shown below:
| Username | Score | | - | - | | userId1 | 11 | | userId2 | 54 | | userId3 | 24 |
These users were added to a sorted set called usersScores using the following Redis commands:
ZADD usersScores 11 "userId1"
ZADD usersScores 54 "userId2"
ZADD usersScores 24 "userId3"
Redis Commander, the web GUI provided on Stackhero with Redis instances
Next, we retrieved userId1's score:
ZSCORE usersScores "userId1"
> 11
This confirmed that the score for userId1 was indeed 11. After that, we checked userId1's rank:
ZREVRANK usersScores "userId1"
> 2
Remember, ranking starts at 0. This means that the rankings are as follows:
| Username | Score | Rank | | - | - | - | | userId1 | 11 | 2 | | userId2 | 54 | 0 | | userId3 | 24 | 1 |
The command ZREVRANK returned 2, which is exactly what we expected for userId1.
You can also fetch the top entries. For example, to retrieve the first 2 users (from rank 0 to rank 1) run:
ZREVRANGE usersScores 0 1 WITHSCORES
> 1) userId2
> 2) 54
> 3) userId3
> 4) 24
To get the top 100 users simply run:
ZREVRANGE usersScores 0 99 WITHSCORES
This approach is efficient and perfectly suited to high-performance real-time ranking.
Other useful commands
Below are some additional Redis commands used on our client's website:
- Get users ranked between positions 50 and 100:
ZREVRANGE usersScores 50 100 WITHSCORES - Add a user:
ZADD usersScores 40 "userId4" - Update a user's score (this replaces the existing
userId4entry):ZADD usersScores 42 "userId4" - Remove a user:
ZREM usersScores "userId4"
Redis code example using Node.js
After validating the concept in Redis Commander, it is time to integrate Redis into real-world code. Our client uses Node.js and below is an example using ioredis as the client:
const Redis = require('ioredis');
(async () => {
// Set Redis credentials
// If you use Stackhero, you will find these on the Stackhero dashboard
const redis = new Redis({
host: '<redisServerHost>',
password: '<redisServerPassword>',
port: <PORT_TLS>, // <PORT_CLEAR> is for clear connections and <PORT_TLS> is for TLS. TLS should be used.
tls: {}, // Provide an empty object to activate TLS
lazyConnect: true
});
// Connect to Redis
await redis.connect();
// Add users
await redis.zadd('usersScores', 11, 'userId1');
await redis.zadd('usersScores', 54, 'userId2');
await redis.zadd('usersScores', 24, 'userId3');
// Retrieve userId1 score
const score = await redis.zscore('usersScores', 'userId1');
console.log('userId1 has ' + score + ' points');
// Retrieve userId1 rank position
const rankPosition = await redis.zrevrank('usersScores', 'userId1');
console.log('userId1 is ranked at position ' + rankPosition);
// Disconnect from Redis
await redis.disconnect();
})();
This simple yet powerful code snippet is ideal for managing real-time ranking data.
Conclusion
Tackling this problem was both interesting and challenging. In our case, Redis proved to be an ideal solution because it is easy to use, powerful, and exceptionally fast.
If you would like to experiment with Redis, you can start an instance on Stackhero in just 2 minutes. Enjoy the latest stable version, a web GUI, backups, and impressive performance right at your fingertips.
Stackhero dashboard showing Node.js and Redis services running