English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Durante o desenvolvimento de sistemas de servidor, para适应大数据并发的请求,我们 often precisamos armazenar dados de forma assíncrona, especialmente quando estamos fazendo sistemas distribuídos. Neste momento, não podemos esperar pela resposta do banco de dados para obter o ID automático ao inserir, mas precisamos gerar um ID único global antes de inserir no banco de dados. O ID único global pode ser usado no servidor de jogo para facilitar a fusão de servidores no futuro, evitando conflitos de chaves. Também pode ser implementado a partitionamento de banco de dados e tabela conforme o crescimento do negócio, por exemplo, um item de um usuário deve estar dentro de um fragmento específico, e este fragmento pode ser determinado pelo valor de faixa do ID do usuário, por exemplo, o ID do usuário é maior que1000 é menor que10Os usuários com 0000 no interior de um fragmento. Atualmente, os mais comuns são os seguintes:
1UUID do Java nativo.
UUID.randomUUID().toString(), pode ser gerado localmente pelo programa de serviço, a geração de ID não depende da implementação do banco de dados.
Vantagens:
Geração local de ID, sem a necessidade de chamadas remotas.
globalmente único e não repetido.
excelente capacidade de expansão horizontal.
Desvantagens:
Os IDs têm128 bits, ocupando muito espaço, precisa ser armazenado como tipo de string, a eficiência do índice é extremamente baixa.
Os IDs gerados não contêm Timestamp, não podem garantir um aumento de tendência, e não são bons para depender no momento de dividir o banco de dados e a tabela.
2baseado no método incr do Redis
O Redis é operado em thread única, e o incr garante uma operação de incrementação atômica. Além disso, suporta a configuração do passo de incrementação.
Vantagens:
Implantação fácil, uso simples, basta chamar uma API do Redis.
Múltiplos servidores podem compartilhar um serviço Redis, reduzindo o tempo de desenvolvimento de dados compartilhados.
O Redis pode ser implantado em clusters para resolver problemas de falha de ponto único.
Desvantagens:
Se o sistema for muito grande, muitos serviços solicitando ao Redis ao mesmo tempo podem causar gargalos de desempenho.
3solução proveniente do Flicker
Esta solução está baseada no ID auto-incrementado do banco de dados, que usa um banco de dados separado para gerar IDs. Detalhes podem ser encontrados na internet, pessoalmente acho que é um pouco complicado, não recomendo usá-lo.
4,Twitter Snowflake
snowflake é um algoritmo de geração de ID distribuído aberto pela Twitter, cuja ideia central é: gerar um ID do tipo long, usando41bit como número de milissegundos,10bit como número da máquina,12bit como sequência de milissegundos dentro do segundo. Este algoritmo pode gerar teoricamente até1000*(2^12) pontos, ou seja, aproximadamente400W ID, pode completamente atender às necessidades do negócio.
Segundo o pensamento do algoritmo snowflake, podemos gerar nosso ID globalmente único com base em nosso cenário de negócios. Porque o tipo long no Java tem64bits,portanto, o ID projetado precisa ser controlado64bits。
Vantagem: Alta performance, baixa latência; Aplicação independente; Ordenado por tempo.
Desvantagem: Precisa de desenvolvimento e implantação independentes.
Por exemplo, o ID que projetamos contém as seguintes informações:
| 41 bits: Timestamp | 3 bits: Região | 10 bits: Número da máquina | 10 bits: Sequência |
Java código para geração de ID único:
/** * Gerador de ID personalizado * Regra de geração de ID: O ID tem 64 bits * * | 41 bits: Timestamp (milissegundos) | 3 bits: Região (data center) | 10 bits: Número da máquina | 10 bits: Sequência | */ public class GameUUID{ // Tempo de referência private long twepoch = 1288834974657L; //Qui, 04 Nov 2010 01:42:54 GMT // Número de bits do sinal de região private final static long regionIdBits = 3L; // Número de bits do identificador da máquina private final static long workerIdBits = 10L; // Número de bits do identificador de sequência private final static long sequenceBits = 10L; // Máximo valor do ID do sinal de região private final static long maxRegionId = -1L ^ (-1L << regionIdBits); // Máximo valor do ID da máquina private final static long maxWorkerId = -1L ^ (-1L << workerIdBits); // Máximo valor do ID de sequência private final static long sequenceMask = -1L ^ (-1L << sequenceBits); // Máquina ID deslocada para a esquerda10bits private final static long workerIdShift = sequenceBits; // deslocamento para a esquerda do ID de negócio2bits 0 private final static long regionIdShift = sequenceBits + workerIdBits; // deslocamento para a esquerda do milissegundo do tempo23bits private final static long timestampLeftShift = sequenceBits + workerIdBits + regionIdBits; private static long lastTimestamp = -1L; private long sequence = 0L; private final long workerId; private final long regionId; public GameUUID(long workerId, long regionId) { // Se estiver fora do intervalo, lance uma exceção if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0"); {} if (regionId > maxRegionId || regionId < 0) { throw new IllegalArgumentException("datacenter Id can't be greater than %d or less than 0"); {} this.workerId = workerId; this.regionId = regionId; {} public GameUUID(long workerId) { // Se estiver fora do intervalo, lance uma exceção if (workerId > maxWorkerId || workerId < 0) { throw new IllegalArgumentException("worker Id can't be greater than %d or less than 0"); {} this.workerId = workerId; this.regionId = 0; {} public long generate() { return this.nextId(false, 0); {} /** * O código gerado realmente * * @param isPadding * @param busId * @return */ private synchronized long nextId(boolean isPadding, long busId) { long timestamp = timeGen(); long paddingnum = regionId; if (isPadding) {}} paddingnum = busId; {} if (timestamp < lastTimestamp) { try { throw new Exception("Clock moved backwards. Refusing to generate id for ") + (lastTimestamp - timestamp) + " milliseconds") } catch (Exception e) { e.printStackTrace(); {} {} //如果上次生成时间和当前时间相同,在同一毫秒内 if (lastTimestamp == timestamp) { //sequence自增,因为sequence只有10bit,所以和sequenceMask相与一下,去掉高位 sequence = (sequence + 1) & sequenceMask; //判断是否溢出,也就是每毫秒内超过1024,当为1024时,与sequenceMask相与,sequence就等于0 if (sequence == 0) { //自旋等待到下一毫秒 timestamp = tailNextMillis(lastTimestamp); {} } else { // 如果和上次生成时间不同,重置sequence,就是下一毫秒开始,sequence计数重新从0开始累加, // 为了保证尾数随机性更大一些,最后一位设置一个随机数 sequence = new SecureRandom().nextInt(10; {} lastTimestamp = timestamp; return ((timestamp - twepoch) << timestampLeftShift) | (paddingnum << regionIdShift) | (workerId << workerIdShift) | sequence; {} // 防止产生的时间比之前的时间还要小(由于NTP回拨等问题),保持增量的趋势。 private long tailNextMillis(final long lastTimestamp) { long timestamp = this.timeGen(); while (timestamp <= lastTimestamp) { timestamp = this.timeGen(); {} return timestamp; {} // Obter o timestamp atual protected long timeGen() { return System.currentTimeMillis(); {} {}
Ponto a ser notado ao usar este método personalizado:
Para manter a tendência de crescimento, evitar que alguns servidores tenham horários antigos e outros tenham horários tardios, é necessário controlar o tempo de todos os servidores e evitar que o servidor de tempo NTP redefina o tempo do servidor; ao atravessar a fronteira de milissegundos, o número de série sempre retorna a 0, o que resulta em muitos números de série começando com 0, causando que os IDs gerados após o módulo não sejam uniformes, então o número de série não retorna a 0 a cada vez, mas a um valor entre 0 e9de números aleatórios.
Os métodos mencionados acima podem ser escolhidos conforme necessário. No desenvolvimento de servidores de jogos, escolha conforme o tipo do seu jogo, por exemplo, jogos móveis podem usar métodos simples como redis, fáceis e sem erros, já que a quantidade de novos IDs gerados por servidores individuais não é grande o suficiente para atender às necessidades. Para servidores de jogos globais em grande escala, que são principalmente distribuídos, pode-se usar o método snowflake, o código snowflake mencionado acima é apenas um exemplo, que precisa ser personalizado conforme necessário, então há um volume adicional de desenvolvimento, e atenção deve ser dada aos itens importantes mencionados acima.
A seguir, apresentamos uma compilação de métodos para gerar IDs únicos globais implementados com código Java para servidores de jogos, esperando que sejam úteis. Se você tiver alguma dúvida, por favor, deixe um comentário, e eu responder-ei o mais rápido possível. Também agradeço muito o apoio ao site Tutorial Yell.
Declaração: O conteúdo deste artigo é extraído da internet, pertencente ao respectivo proprietário. O conteúdo é contribuído e carregado voluntariamente pelos usuários da internet. Este site não possui direitos de propriedade, não foi editado artificialmente e não assume responsabilidade legal relevante. Se você encontrar conteúdo suspeito de violação de direitos autorais, por favor, envie um e-mail para: notice#oldtoolbag.com (ao enviar e-mail, substitua # por @ para denunciar. Forneça também provas relacionadas, e, se confirmadas, o site deletará imediatamente o conteúdo suspeito de violação de direitos autorais.)