When a single sorted-set key holds hundreds of thousands of members, it becomes a hot key and a large key — both of which degrade performance. The TairJedis client provides a DistributedLeaderBoard class that transparently distributes leaderboard data across multiple sub-leaderboards. One logical key fans out to N sub-leaderboards; rank queries aggregate results across all of them and sum the per-sub-leaderboard ranks to produce each member's final rank. This lets you build leaderboards with more than 100,000 members without managing data sharding yourself.
How precise ranking works
exZset supports two methods for computing ranks in a distributed leaderboard: precise ranking (recommended) and linear interpolation (not yet implemented in exZset).
Precise ranking distributes members across N sub-leaderboards using a Cyclic Redundancy Check (CRC) hash of each member's key. To get the rank of member x, FindRank(x) queries every sub-leaderboard and sums the returned ranks. For example, with three sub-leaderboards and 3,000 members total, if the per-sub-leaderboard ranks for member x are 124, 183, and 156, the final rank is 463. Ranks are exact, and the time complexity to retrieve a rank is m × O(log(N)), where m is the number of sub-leaderboards.
Linear interpolation divides data into score segments, records the member count and highest rank for each segment, and estimates ranks using interpolation. Getting a rank is O(m), but ranks are approximate.
Use precise ranking unless your use case can tolerate estimated ranks. For more information about the exZset commands used in this document, see exZset commands.
Prerequisites
Before you begin, ensure that you have:
-
A Tair instance endpoint, port, and password
-
Java development environment with Maven
Set up the leaderboard
Step 1: Add the Maven dependency
Add the following to your pom.xml:
<dependency>
<groupId>com.aliyun.tair</groupId>
<artifactId>alibabacloud-tairjedis-sdk</artifactId>
<version>5.3.1</version>
</dependency>
Step 2: Configure and run the leaderboard
The following example creates a distributed leaderboard, adds six members with multi-dimensional scores (gold, silver, and bronze medal counts), and retrieves one member's rank and the overall top three.
import io.valkey.JedisPool;
import io.valkey.JedisPoolConfig;
import com.aliyun.tair.tairzset.*;
public class DistributedLeaderBoardExample {
// Instance connection settings
private static final int DEFAULT_CONNECTION_TIMEOUT = 5000;
private static final int DEFAULT_SO_TIMEOUT = 2000;
private static final String HOST = "<r-bp1mx0ydsivrbp****.redis.rds.aliyuncs.com>";
private static final int PORT = 6379;
private static final String PASSWORD = "<Pass****word>";
private static final JedisPoolConfig config = new JedisPoolConfig();
// Leaderboard settings
private static final int shardKeySize = 10; // Number of sub-leaderboards
private static final int pageSize = 10; // Members per page
private static final boolean reverse = true; // true = descending order (highest score first)
private static final boolean useZeroIndexForRank = false; // false = ranks start from 1
public static void main(String[] args) {
JedisPool jedisPool = new JedisPool(config, HOST, PORT, DEFAULT_CONNECTION_TIMEOUT,
DEFAULT_SO_TIMEOUT, PASSWORD, 0, null);
// Create the distributed leaderboard
DistributedLeaderBoard dlb = new DistributedLeaderBoard("distributed_leaderboard", jedisPool,
shardKeySize, pageSize, reverse, useZeroIndexForRank);
// Add members with multi-dimensional scores: gold, silver, bronze
// Members are sorted by gold first, then silver, then bronze
dlb.addMember("A", 32, 21, 16);
dlb.addMember("B", 25, 29, 21);
dlb.addMember("C", 20, 7, 12);
dlb.addMember("D", 14, 4, 16);
dlb.addMember("E", 13, 21, 18);
dlb.addMember("F", 13, 17, 14);
// Get the rank of member A
System.out.println(dlb.rankFor("A")); // 1
// Get the top 3 members
System.out.println(dlb.top(3));
// [{"member":"A","score":"32#21#16","rank":1},
// {"member":"B","score":"25#29#21","rank":2},
// {"member":"C","score":"20#7#12","rank":3}]
}
}
Replace the following placeholders with your actual values:
| Placeholder | Description | Example |
|---|---|---|
<r-bp1mx0ydsivrbp****.redis.rds.aliyuncs.com> |
Instance endpoint | r-bp1example.redis.rds.aliyuncs.com |
<Pass****word> |
Instance password | Your instance password |
The score field in the top() response encodes multi-dimensional values separated by #. For member A, "score":"32#21#16" means 32 gold, 21 silver, and 16 bronze medals.
Parameters
| Parameter | Type | Description | Default |
|---|---|---|---|
shardKeySize |
int | Number of sub-leaderboards. Plan this value upfront — it cannot be scaled out dynamically after the leaderboard is created. | 10 |
pageSize |
int | Number of members per page. | 10 |
reverse |
boolean | Sort order. true sorts in descending order (highest score first). false sorts in ascending order. |
false |
useZeroIndexForRank |
boolean | Rank indexing. true starts ranks from 0. false starts ranks from 1. |
true |
For the full list of available methods, see the com.aliyun.tair.tairzset.DistributedLeaderBoard class in the alibabacloud-tairjedis-sdk Javadoc.
Appendix: Conventional vs. distributed leaderboards
The following table compares time complexity for common operations between a conventional leaderboard (single exZset key) and a distributed leaderboard (m sub-leaderboards).
In the distributed column, m is the number of sub-leaderboards and N is the number of members in a sub-leaderboard.
| Operation | Conventional implementation | Conventional complexity | Distributed implementation | Distributed complexity |
|---|---|---|---|---|
| Insert a member | EXZADD |
O(log(N)) | Compute target sub-leaderboard with crc(key) & m, then EXZADD |
O(log(N)) |
| Update a member's score | EXZINCRBY |
O(log(N)) | Compute target sub-leaderboard with crc(key) & m, then EXZINCRBY |
O(log(N)) |
| Remove a member | EXZREM |
O(M\*log(N)) | Compute target sub-leaderboard with crc(key) & m, then EXZREM |
O(log(N)) |
| Count all members | EXZCARD |
O(1) | EXZCARD on each sub-leaderboard, then sum |
O(m) |
| Count total pages | EXZCARD ÷ page size |
O(1) | EXZCARD on each sub-leaderboard, sum, then divide by page size |
O(m) |
| Count members in a score range | EXZCOUNT |
O(log(N)) | EXZCOUNT on each sub-leaderboard, then merge |
m × O(log(N)) |
| Remove members in a score range | EXZREMRANGEBYSCORE |
O(log(N)+M) | EXZREMRANGEBYSCORE on each sub-leaderboard |
m × O(log(N)) |
| Get a member's score | EXZSCORE |
O(1) | Compute target sub-leaderboard with crc(key) & m, then EXZSCORE |
O(1) |
| Get a member's rank | EXZRANK |
O(log(N)) | EXZRANKBYSCORE on each sub-leaderboard, then sum |
m × O(log(N)) |
| Get a member's score and rank | EXZSCORE + EXZRANK |
O(log(N)) | 1. Compute target sub-leaderboard with crc(key) & m, then EXZSCORE. 2. EXZRANKBYSCORE on each sub-leaderboard, then sum. |
m × O(log(N)) |
| Get the top i members | EXZRANGE |
O(log(N)+M) | EXZRANGE top i from each sub-leaderboard, then merge |
m × O(log(N)) |
| Get page i of the leaderboard | EXZRANGE |
O(log(N)) | Retrieve all members before the target page from each sub-leaderboard, then sort | m × O(log(N)) |
| Set an expiration time | EXPIRE |
O(1) | EXPIRE on each sub-leaderboard |
O(m) |
| Delete the leaderboard | DEL |
O(N) | DEL on each sub-leaderboard |
m × O(N) |