Native Redis sorted sets rank members by a single score, which makes medal tables, time-windowed rankings, and other multidimensional leaderboards difficult to implement without workarounds. exZset (also called TairZset) sorts double-type scores across up to 256 dimensions, eliminating the need for complex score concatenation and enabling atomic increments across all dimensions.
Use exZset when you need to:
Rank participants by multiple criteria simultaneously (for example, gold, silver, and bronze medal counts)
Query leaderboards across different time windows — hourly, daily, weekly, or monthly — from a single key
Scale leaderboard workloads to additional shards when storage or compute is constrained
For the full command reference, see exZset commands.
Why exZset instead of native Zset
The native Redis Sorted Set (Zset) supports sorting by a single double-type score. One common workaround is to concatenate scores using the IEEE 754 standard, but that approach has three drawbacks: complex implementation, reduced precision, and the inability to use the ZINCRBY command.
exZset addresses all three:
| Feature | Native Zset | IEEE 754 workaround | exZset |
|---|---|---|---|
| Sorting dimensions | 1 | Multiple (with precision loss) | Up to 256 |
| Atomic increment | ZINCRBY | Not supported | EXZINCRBY |
| API compatibility | — | — | Similar to native Zset |
| distributed leaderboards | No | No | Yes |
| Java client | — | — | Open source TairJedis |
How scores are compared
exZset uses # as a dimension separator and compares scores left to right: it compares the leftmost dimension first, then moves right only when values are equal. If all dimensions are equal, members are sorted by ASCII order.
For a score score1#score2#score3:
Compare
score1. If unequal, this determines the ranking.If
score1is equal, comparescore2. If unequal, this determines the ranking.If
score2is also equal, comparescore3.
Think of # as a decimal point: 0#99 < 99#90 < 99#99 — the leftmost dimension always dominates.
Build a medal table
A medal table ranks participants first by gold medals, then silver, then bronze. With exZset, you encode each participant's medal counts as a three-dimensional score (gold#silver#bronze), so the ranking logic is built into the data structure itself — no application-level sorting required.
Example data:
| Rank | Participant | |||
|---|---|---|---|---|
| 1 | A | 32 | 21 | 16 |
| 2 | B | 25 | 29 | 21 |
| 3 | C | 20 | 7 | 12 |
| 4 | D | 14 | 4 | 16 |
| 5 | E | 13 | 21 | 18 |
| 6 | F | 13 | 17 | 14 |
Participants E and F both have 13 gold medals. E ranks higher because E has 21 silver medals versus F's 17.
Prerequisites
Before you begin, ensure that you have:
A Tair instance endpoint, port, and password
TairJedis SDK added to your project
Add the TairJedis dependency
Add the following to pom.xml:
<dependency>
<groupId>com.aliyun.tair</groupId>
<artifactId>alibabacloud-tairjedis-sdk</artifactId>
<version>5.3.1</version>
</dependency>Insert and query leaderboard data
The following example uses the LeaderBoard class from the TairJedis SDK. The constructor takes the key name, connection pool, page size, and sort options.
import io.valkey.JedisPool;
import io.valkey.JedisPoolConfig;
import com.aliyun.tair.tairzset.LeaderBoard;
public class LeaderBoardExample {
// Replace with your instance endpoint and credentials.
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();
public static void main(String[] args) {
JedisPool jedisPool = new JedisPool(config, HOST, PORT, DEFAULT_CONNECTION_TIMEOUT,
DEFAULT_SO_TIMEOUT, PASSWORD, 0, null);
// Create a leaderboard named "leaderboard".
LeaderBoard lb = new LeaderBoard("leaderboard", jedisPool, 10, true, false);
// Add members with three-dimensional scores: gold, silver, bronze.
lb.addMember("A", 32, 21, 16);
lb.addMember("B", 25, 29, 21);
lb.addMember("C", 20, 7, 12);
lb.addMember("D", 14, 4, 16);
lb.addMember("E", 13, 21, 18);
lb.addMember("F", 13, 17, 14);
// Get the rank of A (1-indexed).
System.out.println(lb.rankFor("A"));
// 1
// Get the top 3.
System.out.println(lb.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}]
// Get the full leaderboard.
System.out.println(lb.allLeaders());
// [{"member":"A","score":"32#21#16","rank":1},
// {"member":"B","score":"25#29#21","rank":2},
// {"member":"C","score":"20#7#12","rank":3},
// {"member":"D","score":"14#4#16","rank":4},
// {"member":"E","score":"13#21#18","rank":5},
// {"member":"F","score":"13#17#14","rank":6}]
}
}For all available LeaderBoard methods, see the alibabacloud-tairjedis-sdk Javadoc under com.aliyun.tair.tairzset.LeaderBoard.
The examples above use Java with TairJedis. For other languages, call the exZset commands directly — see exZset commands.
Build time-windowed leaderboards
You can serve hourly, daily, weekly, and monthly leaderboards from a single key by encoding time into both the score and the member name. This approach uses exZset's left-to-right comparison to filter entries by time range using EXZREVRANGEBYSCORE.
Key and score design
Store all data for a month in one key (for example, julyZset). Each entry encodes time in six dimensions:
score format: month#week#day#hour#minute#points
member format: month#week#day#hour#minute_username| Dimension | Example value | Meaning |
|---|---|---|
month | 7 | July |
week | 2 | Second week of the month |
day | 6 | 6th day of the week |
hour | 16 | 16:00 |
minute | 22 | :22 |
points | 100 | Score earned at that moment |
The time prefix in the member name links each entry to a specific minute. Because exZset compares dimensions left to right, setting the lower dimensions to 0 in EXZREVRANGEBYSCORE creates an inclusive lower bound for any time granularity — the leftmost dimensions act as the primary filter.
Insert data with EXZINCRBY
Use EXZINCRBY to add or increment a member's score atomically — no read-modify-write cycle required.
EXZINCRBY julyZset 7#2#6#16#22#100 7#2#6#16#22_user1
EXZINCRBY julyZset 7#2#6#16#22#50 7#2#6#16#22_user2
EXZINCRBY julyZset 7#2#6#16#23#70 7#2#6#16#23_user1
EXZINCRBY julyZset 7#2#6#16#23#80 7#2#6#16#23_user1Query leaderboards by time window
All range queries use EXZREVRANGEBYSCORE with score bounds matched to the desired time window.
| Leaderboard | Score range | Command | Output |
|---|---|---|---|
| Real-time hourly (15:23–16:23, current time 16:23) | All entries updated in the past 60 minutes | EXZREVRANGEBYSCORE julyZset 7#2#6#16#23#0 7#2#6#15#23#0 | 7#2#6#16#22_user1, 7#2#6#16#22_user2 |
| Specific hour (16:00–17:00) | All entries in the 16:xx window | EXZREVRANGEBYSCORE julyZset 7#2#6#17#0#0 7#2#6#16#0#0 | 7#2#6#16#22_user1, 7#2#6#16#22_user2 |
| Daily (July 5) | All entries on day 5 of week 2 | EXZREVRANGEBYSCORE julyZset 7#2#6#0#0#0 7#2#5#0#0#0 | 7#2#5#10#23_user1 |
| Weekly (week 2 of July) | All entries in week 2 | EXZREVRANGEBYSCORE julyZset 7#3#0#0#0#0 7#2#0#0#0#0 | 7#2#6#16#22_user1, 7#2#6#16#22_user2, 7#2#5#10#23_user1 |
| Monthly (July) | All entries in July | EXZREVRANGEBYSCORE julyZset 7#6#0#0#0#0 7#0#0#0#0#0 | 7#4#20#12#20_user1, 7#2#6#16#22_user1, 7#2#6#16#22_user2, 7#2#5#10#23_user1 |
For frequently accessed rankings such as the real-time hourly leaderboard, cache the results to reduce repeated range queries.
What's next
exZset commands — full command reference for exZset
alibabacloud-tairjedis-sdk — TairJedis SDK source code and additional examples