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 クライアントが必要です。
pom.xml 構成を追加します。
<dependency> <groupId>com.aliyun.tair</groupId> <artifactId>alibabacloud-tairjedis-sdk</artifactId> <version>5.3.1</version> </dependency>サンプルコード。
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_user17#2#6#16#22#100は、7 月の第 2 週の 6 日目の 16:22 にスコアが 100 に更新されたことを示します。7#2#6#16#22_user1は、この時点でスコアが更新されたユーザーを示します。時間プレフィックスがユーザー名に追加されます。
リーダーボードの種類 | コマンドと出力 |
リアルタイムの時間単位のリーダーボード。このタイプのリーダーボードには、現在の時刻より 1 時間以内にスコアが更新されたメンバーが含まれます。たとえば、現在の時刻が 16:23 の場合、リーダーボードには 15:23 から 16:23 の範囲でスコアが更新されたメンバーが含まれます。 説明 ランキング結果に頻繁にアクセスする場合は、ランキング結果をキャッシュすることをお勧めします。 | コマンド: 出力: |
特定の時間のリーダーボード。たとえば、16:00 から 17:00 の時間範囲内にスコアが更新されたメンバーを含むリーダーボードをクエリできます。 | コマンド: 出力: |
日次リーダーボード。たとえば、7 月 5 日にデータが生成されたリーダーボードをクエリできます。 | クエリの前に、次のコマンドを使用して 7 月 5 日に生成されたデータレコードを挿入します。 出力: コマンド: 出力: |
週次リーダーボード。たとえば、7 月の第 2 週のリーダーボードをクエリできます。 | コマンド: 出力: |
月次リーダーボード。たとえば、7 月のリーダーボードをクエリできます。 | クエリの前に、次のコマンドを使用して 7 月 20 日に生成されたデータレコードを挿入します。 出力: コマンド: 出力: |
金メダル
銀メダル
銅メダル