All Products
Search
Document Center

Tair (Redis® OSS-Compatible):Implement multidimensional leaderboards using exZset

Last Updated:Mar 28, 2026

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:

FeatureNative ZsetIEEE 754 workaroundexZset
Sorting dimensions1Multiple (with precision loss)Up to 256
Atomic incrementZINCRBYNot supportedEXZINCRBY
API compatibilitySimilar to native Zset
distributed leaderboardsNoNoYes
Java clientOpen 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:

  1. Compare score1. If unequal, this determines the ranking.

  2. If score1 is equal, compare score2. If unequal, this determines the ranking.

  3. If score2 is also equal, compare score3.

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:

RankParticipant金牌 Gold银牌 Silver铜牌 Bronze
1A322116
2B252921
3C20712
4D14416
5E132118
6F131714

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.

Note

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
DimensionExample valueMeaning
month7July
week2Second week of the month
day66th day of the week
hour1616:00
minute22:22
points100Score 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_user1

Query leaderboards by time window

All range queries use EXZREVRANGEBYSCORE with score bounds matched to the desired time window.

LeaderboardScore rangeCommandOutput
Real-time hourly (15:23–16:23, current time 16:23)All entries updated in the past 60 minutesEXZREVRANGEBYSCORE julyZset 7#2#6#16#23#0 7#2#6#15#23#07#2#6#16#22_user1, 7#2#6#16#22_user2
Specific hour (16:00–17:00)All entries in the 16:xx windowEXZREVRANGEBYSCORE julyZset 7#2#6#17#0#0 7#2#6#16#0#07#2#6#16#22_user1, 7#2#6#16#22_user2
Daily (July 5)All entries on day 5 of week 2EXZREVRANGEBYSCORE julyZset 7#2#6#0#0#0 7#2#5#0#0#07#2#5#10#23_user1
Weekly (week 2 of July)All entries in week 2EXZREVRANGEBYSCORE julyZset 7#3#0#0#0#0 7#2#0#0#0#07#2#6#16#22_user1, 7#2#6#16#22_user2, 7#2#5#10#23_user1
Monthly (July)All entries in JulyEXZREVRANGEBYSCORE julyZset 7#6#0#0#0#0 7#0#0#0#0#07#4#20#12#20_user1, 7#2#6#16#22_user1, 7#2#6#16#22_user2, 7#2#5#10#23_user1
Note

For frequently accessed rankings such as the real-time hourly leaderboard, cache the results to reduce repeated range queries.

What's next