English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Al desarrollar sistemas de sistemas de servidor, para adaptarse a la alta concurrencia de datos, a menudo necesitamos almacenar datos de manera asincrónica, especialmente al desarrollar sistemas distribuidos. En este momento, no podemos esperar a que la inserción en la base de datos devuelva el ID automático, sino que necesitamos generar un ID único global antes de insertar en la base de datos. Al usar un ID único global, en los servidores de juegos, el ID único global puede usarse para facilitar la fusión de servidores en el futuro, y no aparecerán conflictos de claves. También puede implementar la división y tabulación de bases de datos a medida que crezca el negocio, por ejemplo, los artículos de un usuario deben estar en la misma partición, y esta partición puede determinarse según el rango de valores del ID de usuario, por ejemplo, el ID de usuario es mayor que1000 es menor que10Los usuarios de 0000 están en una partición. Actualmente, los más comunes son los siguientes:
1Java UUID integrado.
UUID.randomUUID().toString(), puede generarse localmente por el programa de servicio, la generación de ID no depende de la implementación de la base de datos.
Ventajas:
Generar ID localmente, sin necesidad de realizar llamadas remotas.
Único y no repetido a nivel global.
Buena capacidad de expansión horizontal.
Desventajas:
El ID tiene128 bits, ocupan mucho espacio, necesitan almacenarse como tipo de cadena, la eficiencia de índice es muy baja.
Los ID generados no contienen Timestamp, no pueden garantizar un aumento de tendencia, no es bueno depender de ellos al dividir y tabular la base de datos.
2Basado en el método incr de Redis
Redis es una operación de un solo hilo, y el incr asegura una operación de aumento atómico. Además, admite la configuración de la longitud del aumento.
Ventajas:
Fácil de desplegar, fácil de usar, solo necesita llamar a una API de Redis.
Múltiples servidores pueden compartir un servicio Redis, reduciendo el tiempo de desarrollo de datos compartidos.
Redis puede desplegarse en clúster para resolver problemas de fallo de punto único.
Desventajas:
Si el sistema es demasiado grande, muchas servicios solicitando a Redis simultáneamente pueden causar un cuello de botella de rendimiento.
3Solución de Flicker
Este método de solución se basa en el ID automático incremental de la base de datos, que utiliza una base de datos separada para generar ID. Los detalles pueden encontrarse en línea, personalmente creo que es bastante complicado de usar, no lo recomiendo.
4, Twitter Snowflake
Snowflake is a distributed ID generation algorithm open-sourced by Twitter, and its core idea is: generate a long-type ID, using some41bit as the millisecond number,10bit as the machine number,12bit as the sequence number within milliseconds. This algorithm can theoretically generate up to1000*(2^12) bits, which is about400W ID, which can fully meet the needs of the business.
According to the idea of the snowflake algorithm, we can generate our own global unique ID according to our business scenario. Because the length of the long type in Java is64bits, so the ID we designed needs to be controlled within64bits.
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: Area | 10 bits: Machine number | 10 bits: Sequence number |
Java code to generate a unique ID:
/** * Custom ID generator * ID generation rule: ID is up to 64 bits * * | 41 bits: Timestamp (milliseconds) | 3 bits: Area (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 area identifier 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 area 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 to the left10位 private final static long workerIdShift = sequenceBits; // 业务ID偏左移20位 private final static long regionIdShift = sequenceBits + workerIdBits; // 时间毫秒左移23位 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) { // 如果超出范围则抛出异常 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) { // 如果超出范围则抛出异常 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 { lanzar una nueva Exception("El reloj se movió hacia atrás. Rechazando generar id para " + (lastTimestamp - timestamp) + " milliseconds"); } catch (Exception e) { e.printStackTrace(); {} {} //Si la hora generada anteriormente es la misma que la actual, dentro del mismo milisegundo if (lastTimestamp == timestamp) { //sequence aumenta, ya que sequence solo tiene10bit, por lo que AND con sequenceMask para eliminar los bits altos sequence = (sequence + 1) & sequenceMask; //Determinar si se ha producido un desbordamiento, es decir, superar1024,cuando sea1024en ese momento, AND con sequenceMask, sequence será 0 if (sequence == 0) { //Esperar a que sea el próximo milisegundo timestamp = tailNextMillis(lastTimestamp); {} } else { // Si la hora generada es diferente de la anterior, restablezca sequence, es decir, desde el siguiente milisegundo, el recuento de sequence se reinicia desde 0 // Para aumentar la aleatoriedad del número final, configure el último dígito con un número aleatorio sequence = new SecureRandom().nextInt(10; {} lastTimestamp = timestamp; return ((timestamp - twepoch) << timestampLeftShift) | (paddingnum << regionIdShift) | (workerId << workerIdShift) | sequence; {} // Evitar que la hora generada sea menor que la anterior (debido a problemas como el reajuste de NTP), mantener la tendencia del aumento incremental. private long tailNextMillis(final long lastTimestamp) { long timestamp = this.timeGen(); while (timestamp <= lastTimestamp) { timestamp = this.timeGen(); {} return timestamp; {} // Obtener el timestamp actual protected long timeGen() { return System.currentTimeMillis(); {} {}
Puntos a tener en cuenta al usar este método personalizado:
Para mantener la tendencia de crecimiento, es necesario evitar que algunos servidores tengan el tiempo temprano y otros tengan el tiempo tarde, es necesario controlar el tiempo de todos los servidores y evitar que el servidor de tiempo NTP retroceda el tiempo del servidor; en el momento de la transición de los milisegundos, el número de serie siempre vuelve a 0, lo que hace que haya muchos números de serie 0, lo que hace que los ID generados después de la toma de muestra no sean uniformes, por lo que el número de serie no vuelve a 0 cada vez, sino a un 0 a9número aleatorio.
Las formas mencionadas anteriormente podemos elegir según nuestras necesidades. En el desarrollo del servidor de juegos, según el tipo de juego, por ejemplo, el juego para teléfonos móviles puede usar un método redis simple, que es simple y no fácil de cometer errores, ya que la cantidad de nuevos ID de conexión de un solo servidor de este tipo de juego no es muy grande, y puede satisfacer las necesidades completamente. Y para los servidores de juegos globales a gran escala, que son principalmente distribuidos, por lo que se puede usar el método snowflake, el código snowflake mencionado anteriormente es solo un ejemplo, se necesita personalizar según las necesidades, por lo que hay una cantidad adicional de desarrollo, y se deben prestar atención a las consideraciones mencionadas anteriormente.
El resumen de métodos de implementación de ID únicos globales en el servidor de juegos basados en código Java que el editor le ha presentado a ustedes, espero que les sea útil. Si tienen alguna pregunta, déjenme un mensaje y el editor les responderá a tiempo. También agradezco mucho el apoyo de todos a la página web de tutorial de gritos!
Declaración: El contenido de este artículo se ha obtenido de la red, es propiedad del autor original, el contenido se ha contribuido y subido por los usuarios de Internet, este sitio no posee los derechos de propiedad, no se ha realizado un procesamiento editorial manual y no asume ninguna responsabilidad legal relacionada. Si encuentra contenido sospechoso de infracción de derechos de autor, por favor envíe un correo electrónico a: notice#oldtoolbag.com (al enviar un correo electrónico, reemplace # con @ para denunciar y proporcione evidencia. Una vez confirmado, este sitio eliminará inmediatamente el contenido sospechoso de infracción de derechos de autor.)