Quando uma única chave de sorted set contém centenas de milhares de membros, ela se torna uma hot key e uma large key — ambas degradam o desempenho. O cliente TairJedis fornece a classe DistributedLeaderBoard, que distribui os dados do leaderboard de forma transparente entre múltiplos sub-leaderboards. Uma chave lógica se expande em N sub-leaderboards; as consultas de classificação agregam os resultados de todos eles e somam as classificações por sub-leaderboard para produzir a classificação final de cada membro. Isso permite criar leaderboards com mais de 100.000 membros sem gerenciar o sharding de dados manualmente.
Como funciona a classificação precisa
O exZset oferece dois métodos para calcular classificações em um leaderboard distribuído: classificação precisa (recomendada) e interpolação linear (ainda não implementada no exZset).
A classificação precisa distribui os membros entre N sub-leaderboards usando um hash de Verificação de Redundância Cíclica (CRC) da chave de cada membro. Para obter a classificação do membro x, FindRank(x) consulta cada sub-leaderboard e soma as classificações retornadas. Por exemplo, com três sub-leaderboards e 3.000 membros no total, se as classificações por sub-leaderboard do membro x forem 124, 183 e 156, a classificação final será 463. As classificações são exatas, e a complexidade de tempo para recuperar uma classificação é m × O(log(N)), onde m é o número de sub-leaderboards.
A interpolação linear divide os dados em segmentos de pontuação, registra a contagem de membros e a classificação mais alta de cada segmento, e estima as classificações por interpolação. Obter uma classificação tem complexidade O(m), mas os resultados são aproximados.
Use a classificação precisa, a menos que seu caso de uso tolere classificações estimadas. Para mais informações sobre os comandos exZset utilizados neste tópico, consulte comandos do exZset.
Pré-requisitos
Antes de começar, certifique-se de ter:
O endpoint, a porta e a senha de uma instância Tair
Ambiente de desenvolvimento Java com Maven
Defina o leaderboard
Etapa 1: Adicione a dependência Maven
Adicione o seguinte ao seu pom.xml:
<dependency>
<groupId>com.aliyun.tair</groupId>
<artifactId>alibabacloud-tairjedis-sdk</artifactId>
<version>5.3.1</version>
</dependency>
Etapa 2: Configure e execute o leaderboard
O exemplo a seguir cria um leaderboard distribuído, adiciona seis membros com pontuações multidimensionais (contagens de medalhas de ouro, prata e bronze) e recupera a classificação de um membro e o top três geral.
import io.valkey.JedisPool;
import io.valkey.JedisPoolConfig;
import com.aliyun.tair.tairzset.*;
public class DistributedLeaderBoardExample {
// Instance connection settings
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();
// Leaderboard settings
private static final int shardKeySize = 10; // Number of sub-leaderboards
private static final int pageSize = 10; // Members per page
private static final boolean reverse = true; // true = descending order (highest score first)
private static final boolean useZeroIndexForRank = false; // false = ranks start from 1
public static void main(String[] args) {
JedisPool jedisPool = new JedisPool(config, HOST, PORT, DEFAULT_CONNECTION_TIMEOUT,
DEFAULT_SO_TIMEOUT, PASSWORD, 0, null);
// Create the distributed leaderboard
DistributedLeaderBoard dlb = new DistributedLeaderBoard("distributed_leaderboard", jedisPool,
shardKeySize, pageSize, reverse, useZeroIndexForRank);
// Add members with multi-dimensional scores: gold, silver, bronze
// Members are sorted by gold first, then silver, then bronze
dlb.addMember("A", 32, 21, 16);
dlb.addMember("B", 25, 29, 21);
dlb.addMember("C", 20, 7, 12);
dlb.addMember("D", 14, 4, 16);
dlb.addMember("E", 13, 21, 18);
dlb.addMember("F", 13, 17, 14);
// Get the rank of member A
System.out.println(dlb.rankFor("A")); // 1
// Get the top 3 members
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}]
}
}
Substitua os seguintes placeholders pelos valores reais:
|
Placeholder |
Descrição |
Exemplo |
|
|
Endpoint da instância |
|
|
|
Senha da instância |
A senha da sua instância |
O campo score na resposta de top() codifica valores multidimensionais separados por #. Para o membro A, "score":"32#21#16" significa 32 medalhas de ouro, 21 de prata e 16 de bronze.
Parâmetros
|
Parâmetro |
Tipo |
Descrição |
Padrão |
|
|
int |
Número de sub-leaderboards. Defina este valor antecipadamente — ele não pode ser dimensionado dinamicamente após a criação do leaderboard. |
10 |
|
|
int |
Número de membros por página. |
10 |
|
|
boolean |
Ordem de classificação. |
|
|
|
boolean |
Indexação de classificação. |
|
Para a lista completa de métodos disponíveis, consulte a classe com.aliyun.tair.tairzset.DistributedLeaderBoard no Javadoc do alibabacloud-tairjedis-sdk.
Apêndice: Leaderboards convencionais vs. distribuídos
A tabela a seguir compara a complexidade de tempo para operações comuns entre um leaderboard convencional (chave exZset única) e um leaderboard distribuído (m sub-leaderboards).
Na coluna distribuído, m é o número de sub-leaderboards e N é o número de membros em um sub-leaderboard.
| Operação | Leaderboard convencional | Leaderboard distribuído | ||
|---|---|---|---|---|
| Implementação convencional | Complexidade convencional | Implementação distribuída | Complexidade distribuída | |
| Inserir um membro | EXZADD |
O(log(N)) | Calcule o sub-leaderboard de destino com crc(key) & m, depois EXZADD |
O(log(N)) |
| Atualizar a pontuação de um membro | EXZINCRBY |
O(log(N)) | Calcule o sub-leaderboard de destino com crc(key) & m, depois EXZINCRBY |
O(log(N)) |
| Remover um membro | EXZREM |
O(M\*log(N)) | Calcule o sub-leaderboard de destino com crc(key) & m, depois EXZREM |
O(log(N)) |
| Contar todos os membros | EXZCARD |
O(1) | EXZCARD em cada sub-leaderboard e, depois, soma |
O(m) |
| Contar o total de páginas | EXZCARD ÷ tamanho da página |
O(1) | EXZCARD em cada sub-leaderboard, soma e divisão pelo tamanho da página |
O(m) |
| Contar membros em um intervalo de pontuação | EXZCOUNT |
O(log(N)) | EXZCOUNT em cada sub-leaderboard e, depois, mescla os resultados |
m × O(log(N)) |
| Remover membros em um intervalo de pontuação | EXZREMRANGEBYSCORE |
O(log(N)+M) | EXZREMRANGEBYSCORE em cada sub-leaderboard |
m × O(log(N)) |
| Obter a pontuação de um membro | EXZSCORE |
O(1) | Calcule o sub-leaderboard de destino com crc(key) & m, depois EXZSCORE |
O(1) |
| Obter a classificação de um membro | EXZRANK |
O(log(N)) | EXZRANKBYSCORE em cada sub-leaderboard e, depois, soma |
m × O(log(N)) |
| Obter a pontuação e a classificação de um membro | EXZSCORE + EXZRANK |
O(log(N)) | 1. Calcule o sub-leaderboard de destino com crc(key) & m, depois EXZSCORE. 2. Execute EXZRANKBYSCORE em cada sub-leaderboard e, depois, soma. |
m × O(log(N)) |
| Obter os i primeiros membros | EXZRANGE |
O(log(N)+M) | EXZRANGE dos i primeiros de cada sub-leaderboard e, depois, mescla |
m × O(log(N)) |
| Obter a página i do leaderboard | EXZRANGE |
O(log(N)) | Recupere todos os membros anteriores à página de destino de cada sub-leaderboard e, depois, ordene | m × O(log(N)) |
| Definir um tempo de expiração | EXPIRE |
O(1) | EXPIRE em cada sub-leaderboard |
O(m) |
| Excluir o leaderboard | DEL |
O(N) | DEL em cada sub-leaderboard |
m × O(N) |