Generación de identificadores breves pero únicos legibles / utilizables por humanos

86
  • Necesita manejar> 1000 pero <10000 registros nuevos por día

  • No se pueden utilizar GUID / UUID, números de incremento automático, etc.

  • Idealmente debería tener 5 o 6 caracteres de largo, puede ser alfa por supuesto

  • Quisiera reutilizar algos existentes conocidos, si están disponibles

¿Algo ahí fuera?

Kumar
fuente
¿Por qué no utilizar un INT o BIGINT que se aumente automáticamente? Probablemente sea el más legible y pueda manejar fácilmente el volumen.
Malk
según la Q anterior, tratando de mantenerlo en 5/6 caracteres como máximo y admitir hasta 9999 registros nuevos por día
Kumar
@Kumar - ¿Qué sucede si necesita más de 9999 registros en un día? Su solución propuesta no suena sostenible.
ChaosPandion
@ChaosPandion: Creo que estas son probablemente estimaciones aproximadas de carga / tráfico en lugar de límites estrictos. No estoy seguro de por qué querría establecer un límite arbitrario en la cantidad de transacciones diarias.
Paul Sasik
Podrías codificarlo en base 64 y usarlo. No estoy seguro de que pueda reducirlo más pequeño que eso y aún usar caracteres legibles. Pero yo diría que la base 64 es mucho menos legible que la base 32 porque requiere agregar un calificador adicional a la mayoría de los caracteres (f mayúscula, o inferior, o inferior versus solo f, oo).
Malk

Respuestas:

118

Tinyurl y bit.ly utilizan Base 62 para las URL abreviadas. Es un método bien conocido para crear ID "únicos" legibles por humanos. Por supuesto, tendrá que almacenar los ID creados y verificar si hay duplicados en la creación para garantizar la singularidad. (Vea el código al final de la respuesta)

Métricas de singularidad base 62

5 caracteres en la base 62 le darán 62 ^ 5 ID únicos = 916,132,832 (~ 1 mil millones) Con 10k ID por día, estará bien durante 91k + días

6 caracteres en la base 62 le darán 62 ^ 6 ID únicos = 56,800,235,584 (más de 56 mil millones) Con 10k ID por día, estará bien durante más de 5 millones de días

Métricas de singularidad base 36

6 caracteres le darán 36 ^ 6 ID únicos = 2,176,782,336 (2+ mil millones)

7 caracteres le darán 36 ^ 7 ID únicos = 78,364,164,096 (78+ mil millones)

Código:

public void TestRandomIdGenerator()
{
    // create five IDs of six, base 62 characters
    for (int i=0; i<5; i++) Console.WriteLine(RandomIdGenerator.GetBase62(6));

    // create five IDs of eight base 36 characters
    for (int i=0; i<5; i++) Console.WriteLine(RandomIdGenerator.GetBase36(8));
}

public static class RandomIdGenerator 
{
    private static char[] _base62chars = 
        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
        .ToCharArray();

    private static Random _random = new Random();

    public static string GetBase62(int length) 
    {
        var sb = new StringBuilder(length);

        for (int i=0; i<length; i++) 
            sb.Append(_base62chars[_random.Next(62)]);

        return sb.ToString();
    }       

    public static string GetBase36(int length) 
    {
        var sb = new StringBuilder(length);

        for (int i=0; i<length; i++) 
            sb.Append(_base62chars[_random.Next(36)]);

        return sb.ToString();
    }
}

Salida:

z5KyMg
wd4SUp
uSzQtH
UPrGAT
UIf2IS

QCF9GNM5
0UV3TFSS
3MG91VKP
7NTRF10T
AJK3AJU7
Paul Sasik
fuente
3
se ve fantástico, ¿algo que no distinga entre mayúsculas y minúsculas?
Kumar
2
Si desea evitar la distinción entre mayúsculas y minúsculas, puede usar la base 36: codeproject.com/Articles/10619/Base-36-type-for-NET-C, pero para obtener tantas permutaciones como base 62, necesitaría usar más caracteres en su CARNÉ DE IDENTIDAD. Es una compensación. O podría intentar usar otros caracteres además de alfa, pero eso se pone feo para los usuarios.
Paul Sasik
2
aquí stackoverflow.com/questions/9543892/… y muchas gracias
Kumar
11
Un pensamiento. Quizás quitar las vocales para evitar la generación accidental de malas palabras. Especialmente si se enfrenta al público.
Damien Sawyer
4
Dependiendo de dónde esté usando esto (particularmente si se espera que los humanos lean y vuelvan a ingresar los códigos), es posible que desee considerar eliminar los caracteres confusos de consideración: 0 / O e I / l / 1. Esto se puede mitigar en algunos casos con una buena elección de fuente, pero no puedo decir a partir de la pregunta si el OP tendrá control sobre eso.
GrandOpener
17

Recomiendo http://hashids.org/ que convierte cualquier número (por ejemplo, DB ID) en una cadena (usando salt).

Permite decodificar esta cadena de nuevo al número. Por lo que no es necesario almacenarlo en la base de datos.

Tiene libs para JavaScript, Ruby, Python, Java, Scala, PHP, Perl, Swift, Clojure, Objective-C, C, C ++ 11, Go, Erlang, Lua, Elixir, ColdFusion, Groovy, Kotlin, Nim, VBA, CoffeeScript y para Node.js y .NET.

Slawa
fuente
1
¿Puede proporcionar otras opciones similares a su propuesta? - - Es muy interesante. Me gustaría saber si hay opciones predeterminadas como esa en PostgreSQL.
Léo Léopold Hertz 준영
1
Aquí está la versión .NET , pero ¿puede explicar cómo funciona sin necesidad de almacenarlo en la base de datos? ¿Puedo generar randoms únicos sin dar números como entrada y sin sal?
shaijut
@Slawa Necesito algo como hashids para .NET pero el hash final se almacenará en la base de datos en una columna con una longitud fija, ¿es posible decir que siempre generar hash con una longitud máxima de N?
Anon Dev
6

Tenía requisitos similares a los del OP. Busqué en las bibliotecas disponibles, pero la mayoría de ellas se basan en la aleatoriedad y no quería eso. Realmente no pude encontrar nada que no estuviera basado en aleatorio y aún muy corto ... Así que terminé rodando el mío basado en la técnica que usa Flickr , pero modificado para requerir menos coordinación y permitir períodos más largos sin conexión.

En breve:

  • Un servidor central emite bloques de ID que constan de 32 ID cada uno
  • El generador de ID local mantiene un grupo de bloques de ID para generar un ID cada vez que se solicita uno. Cuando el grupo se agota, obtiene más bloques de ID del servidor para llenarlo nuevamente.

Desventajas:

  • Requiere coordinación central
  • Los ID son más o menos predecibles (menos que los ID de base de datos normales, pero no son aleatorios)

Ventajas

  • Se mantiene dentro de los 53 bits (tamaño máximo de Javascript / PHP para números enteros)
  • identificaciones muy cortas
  • Base 36 codificada muy fácil de leer, escribir y pronunciar para los humanos
  • Los ID se pueden generar localmente durante mucho tiempo antes de necesitar contactar nuevamente con el servidor (dependiendo de la configuración del grupo)
  • Teóricamente no hay posibilidad de colisiones

He publicado tanto una biblioteca Javascript para el lado del cliente como una implementación de servidor Java EE. La implementación de servidores en otros idiomas también debería ser fácil.

Aquí están los proyectos:

suid : ID únicos de servicio distribuidos que son breves y fáciles

suid-server-java : implementación de suid-server para la pila de tecnología Java EE.

Ambas bibliotecas están disponibles bajo una licencia liberal de código abierto Creative Commons. Esperando que esto pueda ayudar a otra persona a buscar identificaciones únicas breves.

Stijn de Witt
fuente
¿Puede comparar stackoverflow.com/a/29372036/54964 con su propuesta suid?
Léo Léopold Hertz 준영
1
Se basa en números aleatorios. De hecho, es bastante bueno. Pero sus identificaciones no serán tan cortas como pueden ser. Escribí SUID para comenzar a numerar en 1, por lo que comenzará con identificaciones extremadamente cortas . Piense en 3 o 4 caracteres. Además, tiene otras ventajas interesantes tener ID (más o menos) ordenados de forma incremental, además de empezar con los realmente cortos.
Stijn de Witt
3

Usé la base 36 cuando resolví este problema para una aplicación que estaba desarrollando hace un par de años. Necesitaba generar un número razonablemente único legible por humanos (dentro del año calendario actual de todos modos). Elegí usar el tiempo en milisegundos desde la medianoche del 1 de enero del año en curso (por lo que cada año, las marcas de tiempo podrían duplicarse) y convertirlo a un número base 36. Si el sistema que se estaba desarrollando se encontraba con un problema fatal, generaba el número base 36 (7 caracteres) que se mostraba a un usuario final a través de la interfaz web, quien luego podía transmitir el problema encontrado (y el número) a una persona de soporte técnico (que luego podría usarlo para encontrar el punto en los registros donde comenzó el seguimiento de la pila). Un número como 56af42g7es infinitamente más fácil de leer y transmitir para un usuario que una marca de tiempo como 2016-01-21T15: 34: 29.933-08: 00 o un UUID aleatorio como 5f0d3e0c-da96-11e5-b5d2-0a1d41d68578 .

Warren Smith
fuente
4
¿Puede proporcionar un pseudocódigo en forma estructurada sobre su propuesta? Suena interesante.
Léo Léopold Hertz 준영
0

Realmente me gusta la simplicidad de simplemente codificar un GUID usando el formato Base64 y truncar el == final para obtener una cadena de 22 caracteres (se necesita una línea de código y siempre se puede convertir de nuevo a GUID). Lamentablemente, a veces incluye caracteres + y /. Está bien para la base de datos, no es genial para las URL, pero me ayudó a apreciar las otras respuestas :-)

Desde https://www.codeproject.com/Tips/1236704/Reducing-the-string-Length-of-a-Guid por Christiaan van Bergen

Descubrimos que convertir el Guid (16 bytes) en una representación ASCII usando Base64 resultó en un ID de mensaje utilizable y aún único de solo 22 caracteres.

var newGuid = Guid.NewGuid();
var messageID = Convert.ToBase64String(newGuid.ToByteArray());

var message22chars = Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Substring(0,22);

Por ejemplo: el Guid 'e6248889-2a12-405a-b06d-9695b82c0a9c' (longitud de la cadena: 36) obtendrá una representación Base64: 'iYgk5hIqWkCwbZaVuCwKnA ==' (longitud de la cadena: 24)

La representación de Base64 termina con los caracteres '=='. Podría simplemente truncarlos, sin ningún impacto en la singularidad. Dejándote con un identificador de solo 22 caracteres de longitud.

Ekus
fuente