English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Nel processo di sviluppo del sistema del server, per adattarsi alle richieste di alta concentrazione di dati, spesso dobbiamo eseguire il salvataggio asincrono dei dati, specialmente quando si sviluppa un sistema distribuito, in questo caso non si può aspettare che il database risponda prima di generare l'ID automatico, ma è necessario generare un ID globale unico prima di inserire nel database, utilizzare l'ID globale unico, nel server del gioco, l'ID globale unico può essere utilizzato per facilitare la fusione dei server in futuro, non appariranno conflitti di chiave. Può anche implementare la divisione del database e delle tabelle quando la crescita del business è in aumento, ad esempio, gli oggetti di un utente devono essere messi in uno stesso segmento, e questo segmento potrebbe essere determinato dal valore di intervallo dell'ID utente, ad esempio, gli utenti con ID superiori a 1000 e inferiori a 100000 sono nello stesso segmento. Al momento, le soluzioni più comuni sono le seguenti:
1, UUID integrato in Java
UUID.randomUUID().toString(), può essere generato localmente dal programma di servizio, la generazione dell'ID non dipende dall'implementazione del database.
Vantaggio:
Genera ID localmente, non è necessario effettuare chiamate remote.
Unico e non ripetuto globalmente.
Ha una buona capacità di espansione orizzontale.
Svantaggio:
L'ID ha 128 bits, occupa molto spazio, deve essere salvato come tipo di stringa, l'efficienza dell'indice è estremamente bassa.
Gli ID generati non contengono Timestamp, non possono garantire l'aumento tendenziale, non è facile dipendere da essi quando si dividono il database e le tabelle.
2, Metodo incr basato su Redis
Redis è un'operazione a singolo thread, e l'incr garantisce anche un'operazione di incrementazione atomica. Inoltre, supporta la impostazione della dimensione dell'incremento.
Vantaggio:
Facile da implementare, facile da usare, è sufficiente chiamare l'api di redis.
Più server possono condividere un servizio redis, riducendo il tempo di sviluppo dei dati condivisi.
Redis può essere distribuito in cluster, risolvendo il problema del singolo punto di fallimento.
Svantaggio:
Se il sistema è troppo grande, molte servizi richiedono contemporaneamente a redis, causando un collo di bottiglia di prestazioni.
3, Soluzione proveniente da Flicker
Questa soluzione si basa sull'ID auto-incrementale del database, utilizza un database separato per generare ID. Maggiori dettagli possono essere trovati online, penso che sia piuttosto complicato da usare, non consiglio di utilizzarlo.
4, Twitter Snowflake
Snowflake è un algoritmo di generazione di ID distribuito open source di Twitter, il cui pensiero centrale è: generare un ID di tipo long, utilizzare 41bit come millisecondi, 10bit come numero di macchina e 12bit come numero di sequenza nel millisecondo. Questo algoritmo può generare teoricamente fino a 1000*(2^12) di ID al secondo su un singolo computer, ovvero circa 4 milioni di ID, che può completamente soddisfare le esigenze aziendali.
Secondo l'idea dell'algoritmo snowflake, possiamo generare il nostro ID globale unico in base alla nostra scena di business. Poiché la lunghezza del tipo long in Java è di 64bits, l'ID che progettiamo deve essere controllato entro 64bits.
Advantages: high performance, low latency; independent application; ordered by time.
Disadvantages: requires independent development and deployment.
For example, the ID we designed includes the following information:
| 41 bits: Timestamp | 3 bits: Region | 10 bits: Machine number | 10 bits: Sequence number |
Java code to generate a unique ID:
/** * Custom ID generator * ID generation rule: ID is 64 bits long * * | 41 bits: Timestamp (milliseconds) | 3 bits: Region (data center) | 10 bits: Machine number | 10 bits: Sequence number | */ public class GameUUID{ // Base time private long twepoch = 1288834974657L; //Thu, 04 Nov 2010 01:42:54 GMT // Number of bits for region flag private final static long regionIdBits = 3L; // Number of bits for machine identifier private final static long workerIdBits = 10L; // Number of bits for sequence ID private final static long sequenceBits = 10L; // Maximum value of region flag ID private final static long maxRegionId = -1L ^ (-1L << regionIdBits); // Maximum value of machine ID private final static long maxWorkerId = -1L ^ (-1L << workerIdBits); // Maximum value of sequence ID private final static long sequenceMask = -1L ^ (-1L << sequenceBits); // Machine ID shifted left by 10 bits private final static long workerIdShift = sequenceBits; // Business ID shifted left by 20 bits private final static long regionIdShift = sequenceBits + workerIdBits; // Time milliseconds shifted left by 23 bits 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 fuori dai limiti, lancia un'eccezione 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 fuori dai limiti, lancia un'eccezione 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); {} /** * 实际产生代码的 * * @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; {} // Ottenere il timestamp corrente protected long timeGen() { return System.currentTimeMillis(); {} {}
Quando si utilizza questo metodo personalizzato, è necessario prestare attenzione ai seguenti punti:
Per mantenere la tendenza alla crescita, è necessario evitare che alcuni server abbiano l'ora antica e altri l'ora tarda, è necessario controllare l'ora di tutti i server e evitare che il server di time server NTP torni indietro il tempo del server; Durante il passaggio tra i millisecondi, il numero di serie è sempre riportato a 0, il che farà sì che ci siano molti numeri di serie 0, il che causerà che l'ID generato non sia uniforme dopo il modulo, quindi il numero di serie non viene riportato a 0 ogni volta, ma a un numero casuale tra 0 e 9.
I metodi menzionati sopra possono essere selezionati a seconda delle nostre esigenze. Nel sviluppo del server di gioco, scegliere in base al tipo di gioco, ad esempio, per i giochi mobili, può essere utilizzato un modo redis semplice, semplice e non facile da sbagliare, poiché la quantità di ID nuovi connessi in modo consecutivo non è molto grande, può soddisfare le esigenze. Per i server di gioco mondiali di grandi dimensioni, è principalmente distribuito, quindi può essere utilizzato il modo snowflake, il codice snowflake sopra menzionato è solo un esempio, è necessario personalizzarlo in base alle proprie esigenze, quindi c'è una quantità aggiuntiva di sviluppo, e si dovrebbe prestare attenzione alle preoccupazioni menzionate sopra.
Come riportato sopra, l'editor ha raccolto un riassunto dei metodi di generazione di ID unici globali implementati con codice Java per server di gioco, sperando che sia utile a tutti. Se avete qualsiasi domanda, lasciate un commento, l'editor risponderà tempestivamente. In questo senso, anche grazie per il supporto del sito web delle lezioni urla!
Dichiarazione: il contenuto di questo articolo è stato raccolto da Internet, il copyright è dell'autore originale, il contenuto è stato contribuito e caricato autonomamente dagli utenti di Internet, questo sito non possiede il diritto di proprietà, non è stato elaborato manualmente e non assume responsabilità per le relative responsabilità legali. Se trovi contenuti sospetti di violazione del copyright, ti preghiamo di inviare una e-mail a: notice#oldtoolbag.com (al momento dell'invio dell'e-mail, sostituisci # con @) per segnalare, fornendo prove pertinenti. Una volta verificata, questo sito cancellerà immediatamente il contenuto sospetto di violazione del copyright.