藉助TairJedis用戶端,您可以實現分布式架構熱門排行榜(exZset資料結構)的功能。在操作過程中,您只需關注、操作一個Key,而Tair會自動將資料以及計算任務分布至多個子Key中。預設情況下,子Key的數量為10,您無需關注這些子Key的具體情況。通過本方案,您可以輕鬆實現超10萬個元素的熱門排行榜,從而有效避免產生超大Key和熱Key。
背景資訊
實現分布式架構熱門排行榜有精確排名法和非精確排名法(線性插值法)兩種解決方案。
表 1. 實現分布式架構熱門排行榜的解決方案
解決方案 | 說明 |
精確排名法(推薦) | 將資料分別分配到在不同的Key上進行計算,查詢時,查詢目標資料在各Key中的排名並進行匯總。 例如自訂底層為3個Key,建立一個熱門排行榜(Key)並插入3,000個值(成員),Tair會將3,000個值分別分配到3個Key(子熱門排行榜)中。查詢時,FindRank(x) 分別在3個Key(子熱門排行榜)中擷取到了3個排名:124、183和156,表示x分別在3個Key中排在第124、183和156位,那麼x的實際排名為463(即124+183+156)。
|
線性插值法(exZset暫未實現) | 將資料進行分段,記錄每個區間的數量和最高排名(即此區間中的最高排名),對於區間中介於當前區間最低與最高之間的分數,可通過線性插值法進行估算排名。
|
本方案將使用精確排名法實現分布式架構排行。關於本方案中使用的exZset相關命令,請參見exZset。
程式碼範例
本方案需使用TairJedis(Tair自研)用戶端。
添加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.*; public class DistributedLeaderBoardExample { // 配置執行個體串連地址、連接埠號碼、帳號密碼等資訊。 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(); // 配置分布式熱門排行榜資訊。 private static final int shardKeySize = 10; // 底層子熱門排行榜的數量。 private static final int pageSize = 10; // 熱門排行榜每頁包含的個數。 private static final boolean reverse = true; // 本樣本為從大到小排序。 private static final boolean useZeroIndexForRank = false; // 本樣本以1作為排名起點。 public static void main(String[] args) { JedisPool jedisPool = new JedisPool(config, HOST, PORT, DEFAULT_CONNECTION_TIMEOUT, DEFAULT_SO_TIMEOUT, PASSWORD, 0, null); // 建立分布式架構熱門排行榜,此次以Key名稱distributed_leaderboard為例。 DistributedLeaderBoard dlb = new DistributedLeaderBoard("distributed_leaderboard", jedisPool, shardKeySize, pageSize, reverse, useZeroIndexForRank); // 如果金牌數相同,按照銀牌數排序,否則繼續按照銅牌排序。 // 金牌 銀牌 銅牌 dlb.addMember("A", 32, 21, 16); dlb.addMember("D", 14, 4, 16); dlb.addMember("C", 20, 7, 12); dlb.addMember("B", 25, 29, 21); dlb.addMember("E", 13, 21, 18); dlb.addMember("F", 13, 17, 14); // 擷取A的排名。 dlb.rankFor("A"); // 1 System.out.println(dlb.rankFor("A")); // 擷取top3 dlb.top(3); 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}] } }參數說明:
參數
類型
說明
shardKeySize
int
底層子熱門排行榜的數量,預設為10,無法動態擴容,需要在業務初期做好規劃。
pageSize
int
熱門排行榜每頁包含的個數,預設為10。
reverse
boolean
取值說明:
false(預設):按從小到大排序。
true:按從大到小排序。
useZeroIndexForRank
boolean
取值說明:
true(預設):以0作為排名起點。
false:以1作為排名起點。
更多操作請參見alibabacloud-tairjedis-sdk中的com.aliyun.tair.tairzset.DistributedLeaderBoard介紹。
附錄:常規熱門排行榜和分布式架構熱門排行榜對比
在實現同一準系統時,常規熱門排行榜和分布式架構熱門排行榜的實現方案如下:
準系統 | 常規熱門排行榜 | 分布式架構熱門排行榜 | ||
實現方案 | 時間複雜度 | 實現方案 | 時間複雜度 | |
插入成員 | 通過EXZADD插入元素。 | O(log(N)) | 通過 | O(log(N)) |
更新成員分值 | 通過EXZINCRBY更新分數。 | O(log(N)) | 通過 | O(log(N)) |
移除成員 | 通過EXZREM移除成員。 | O(M*log(N)) | 通過 | O(log(N)) |
查詢成員數量 | 通過EXZCARD查詢成員數量。 | O(1) | 分別通過EXZCARD查詢成員數量,然後相加。 | O(m) 說明 本列中的m代表分區數。 |
查詢總頁數 | 通過EXZCARD查詢成員數量,然後除以PAGE_SIZE(每頁可展示的記錄數)。 | O(1) | 分別通過EXZCARD查詢成員數量,然後相加,得出的結果再除以PAGE_SIZE(每頁可展示的記錄數)。 | O(m) |
某分數範圍內的總成員數 | 通過EXZCOUNT查詢。 | O(log(N)) | 分別執行EXZCOUNT,然後合并結果。 | m*O(log(N)) |
移除某分數範圍內的成員 | 通過EXZREMRANGEBYSCORE移除。 | O(log(N)+M) | 分別執行EXZREMRANGEBYSCORE。 | m*O(log(N)) |
擷取成員分值 | 通過EXZSCORE查詢。 | O(1) | 通過 | O(1) |
擷取成員排名 | 通過EXZRANK查詢。 | O(log(N)) | 在每一個Key(子熱門排行榜)調用 EXZRANKBYSCORE,然後相加。 | m*O(log(N)) |
同時擷取成員分值和排名 | 通過EXZSCORE和EXZRANK查詢。 | O(log(N)) |
| m*O(log(N)) |
查詢第top i個成員 | 通過EXZRANGE查詢。 | O(log(N)+M) | 通過EXZRANGE查詢每個top i,然後合并並得出最終結果。 | m*O(log(N)) |
擷取熱門排行榜第 i 頁 | 通過EXZRANGE查詢。 | O(log(N)) | 將擷取頁數之前的元素都擷取到,然後排序以獲得最終的頁數。 | m*O(log(N)) |
設定到期時間 | 通過EXPIRE設定。 | O(1) | 給每個元素設定到期。 | O(m) |
刪除熱門排行榜 | 通過DEL刪除。 | O(N) | 同時刪除Key中的每個元素。 | m * O(N) |