すべてのプロダクト
Search
ドキュメントセンター

Tair (Redis® OSS-Compatible):exZset を使用した多次元リーダーボードの実装

最終更新日:Nov 09, 2025

exZset は Alibaba Cloud が開発したデータ構造で、最大 256 ディメンションにわたって double 型のスコアをソートできます。

背景情報

ネイティブの Redis Sorted Set (Zset) データ構造は、1 つのディメンションでのみ double 型のスコアに基づくソートをサポートしているため、多次元ソートの実装は困難です。たとえば、IEEE 754 標準を使用して、多次元ソート用のスコアデータを連結できます。ただし、このメソッドには、実装が複雑である、精度が低下する、ZINCRBY コマンドを使用できないなどのいくつかの制限があります。

exZset の特徴

Alibaba Cloud が開発した exZset データ構造は、多次元ソートを簡単に実装するのに役立ち、従来のソリューションに比べて次の利点があります。

  • 最大 256 ディメンションにわたる double 型のスコアのソートをサポートします。ソートの優先順位は左から右です。

    多次元スコアの場合、左側のスコアは右側のスコアよりも優先順位が高くなります。たとえば、3 次元のスコア score1#score2#score3 を考えてみましょう。exZset がスコアを比較するとき、最初に score1 を比較します。score1 の値が等しい場合にのみ score2 を比較します。そうでない場合、score1 の比較結果がスコア全体の最終的な比較結果になります。同様に、score2 の値が等しい場合にのみ score3 を比較します。すべてのディメンションのスコアが同じ場合、要素は ASCII 順にソートされます。

    これを理解しやすくするために、ハッシュ記号 (#) を小数点 (.) と考えることができます。たとえば、0#99、99#90、99#99 の関係は 0.99 < 99.90 < 99.99 と理解できます。これは 0#99 < 99#90 < 99#99 を意味します。

  • 現在のデータを取得し、ローカルで値を追加してから、連結した結果を Tair に書き戻す必要がなくなる EXZINCRBY コマンドをサポートします。

  • ネイティブの Zset と同様の API を提供します。

  • 標準リーダーボード分散アーキテクチャリーダーボード の両方の機能を提供します。

  • オープンソースの TairJedis クライアントを提供します。エンコーディングやデコーディングのカプセル化は必要ありません。また、オープンソースコードを参照して、他の言語でカプセル化を実装することもできます。

説明

このトピックで言及されている exZset コマンドの詳細については、「exZset」をご参照ください。

シナリオ

ソートは、ゲーム、アプリケーション、メダルテーブルのリーダーボードで一般的な要件です。ソートに関する一般的なビジネス要件には、次のものがあります。

  • 作成、読み取り、更新、削除 (CRUD) 操作、逆順ソート、および指定されたスコア範囲に基づくメンバークエリのサポート。

  • ソート結果の迅速な取得。

  • 分散リーダーボード を実装するためのスケーラビリティ。現在のデータシャードのストレージまたは計算能力が不十分な場合、ワークロードを他のデータシャードにオフロードできます。

メダルテーブルの実装

メダルリーダーボードでは、参加者は金、銀、銅メダルの数に基づいてランク付けされます。最初に金メダルの数でソートされます。金メダルの数が同じ場合は、銀メダルの数でソートされます。銀メダルの数も同じ場合は、銅メダルの数でソートされます。この例では、参加者 E と F は金メダルの数が同じですが、参加者 E は参加者 F よりも銀メダルの数が多いため、参加者 E のランクが高くなります。exZset の多次元ソート機能を使用すると、簡単な API を使用してこの要件を満たすことができます。

ランク

参加者

金牌金メダル

银牌銀メダル

铜牌銅メダル

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

コード例

このソリューションには、Tair によって開発された TairJedis クライアントが必要です。

  1. pom.xml 構成を追加します。

            <dependency>
                <groupId>com.aliyun.tair</groupId>
                <artifactId>alibabacloud-tairjedis-sdk</artifactId>
                <version>5.3.1</version>
            </dependency>
  2. サンプルコード。

    import io.valkey.JedisPool;
    import io.valkey.JedisPoolConfig;
    import com.aliyun.tair.tairzset.LeaderBoard;
    
    public class LeaderBoardExample {
        // インスタンスのエンドポイント、ポート番号、パスワード、およびその他の情報を構成します。
        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);
    
            // リーダーボードを作成します。
            LeaderBoard lb = new LeaderBoard("leaderboard", jedisPool, 10, true, false);
    
            // 金メダルの数が同じ場合は、銀メダルの数でソートします。銀メダルの数も同じ場合は、銅メダルの数でソートします。
            //                    金 銀 銅
            lb.addMember("A",     32,  21, 16);
            lb.addMember("D",     14,  4,  16);
            lb.addMember("C",     20,  7,  12);
            lb.addMember("B",     25,  29, 21);
            lb.addMember("E",     13,  21, 18);
            lb.addMember("F",     13,  17,  14);
    
            // A のランクを取得します。
            lb.rankFor("A"); // 1
            System.out.println(lb.rankFor("A"));
    
            // 上位 3 件を取得します。
            lb.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}]
    
            // リーダーボード全体を取得します。
            lb.allLeaders();
            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}]
        }
    } 

    操作の詳細については、「alibabacloud-tairjedis-sdk ドキュメント」の com.aliyun.tair.tairzset.LeaderBoard セクションをご参照ください。

TairZset を使用して、時間、日、週、月ごと、またはリアルタイムのリーダーボードを実装する

キーの月間リーダーボードを実装する場合、月の情報をインデックスとして使用する必要があります。

TairZset データ構造によって提供される多階層インデックスを使用して、さまざまな時間範囲のリーダーボードを実装できます。この例では、月のすべてのデータが julyZset という名前のキーに格納されます。次のコードは、サンプルデータをキーに書き込む方法を示しています。

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
説明
  • 7#2#6#16#22#100 は、7 月の第 2 週の 6 日目の 16:22 にスコアが 100 に更新されたことを示します。

  • 7#2#6#16#22_user1 は、この時点でスコアが更新されたユーザーを示します。時間プレフィックスがユーザー名に追加されます。

リーダーボードの種類

コマンドと出力

リアルタイムの時間単位のリーダーボード。このタイプのリーダーボードには、現在の時刻より 1 時間以内にスコアが更新されたメンバーが含まれます。たとえば、現在の時刻が 16:23 の場合、リーダーボードには 15:23 から 16:23 の範囲でスコアが更新されたメンバーが含まれます。

説明

ランキング結果に頻繁にアクセスする場合は、ランキング結果をキャッシュすることをお勧めします。

コマンド:

EXZREVRANGEBYSCORE julyZset 7#2#6#16#23#0 7#2#6#15#23#0

出力:

1) "7#2#6#16#22_user1"
2) "7#2#6#16#22_user2"

特定の時間のリーダーボード。たとえば、16:00 から 17:00 の時間範囲内にスコアが更新されたメンバーを含むリーダーボードをクエリできます。

コマンド:

EXZREVRANGEBYSCORE julyZset 7#2#6#17#0#0 7#2#6#16#0#0

出力:

1) "7#2#6#16#22_user1"
2) "7#2#6#16#22_user2"

日次リーダーボード。たとえば、7 月 5 日にデータが生成されたリーダーボードをクエリできます。

クエリの前に、次のコマンドを使用して 7 月 5 日に生成されたデータレコードを挿入します。

EXZINCRBY julyZset 7#2#5#10#23#70 7#2#5#10#23_user1

出力:

"7#2#5#10#23#70"

コマンド:

EXZREVRANGEBYSCORE julyZset 7#2#6#0#0#0 7#2#5#0#0#0

出力:

1) "7#2#5#10#23_user1"

週次リーダーボード。たとえば、7 月の第 2 週のリーダーボードをクエリできます。

コマンド:

EXZREVRANGEBYSCORE julyZset 7#3#0#0#0#0 7#2#0#0#0#0

出力:

1) "7#2#6#16#22_user1"
2) "7#2#6#16#22_user2"
3) "7#2#5#10#23_user1"

月次リーダーボード。たとえば、7 月のリーダーボードをクエリできます。

クエリの前に、次のコマンドを使用して 7 月 20 日に生成されたデータレコードを挿入します。

EXZINCRBY julyZset 7#4#20#12#20#50 7#4#20#12#20_user1

出力:

"7#4#20#12#20#50"

コマンド:

EXZREVRANGEBYSCORE julyZset 7#6#0#0#0#0 7#0#0#0#0#0

出力:

1) "7#4#20#12#20_user1"
2) "7#2#6#16#22_user1"
3) "7#2#6#16#22_user2"
4) "7#2#5#10#23_user1"