Generowanie czytelnych / użytecznych, krótkich, ale unikalnych identyfikatorów

86
  • Konieczność obsługi> 1000, ale <10000 nowych rekordów dziennie

  • Nie można używać identyfikatorów GUID / UUID, numerów automatycznego zwiększania itp.

  • Idealnie powinno być 5 lub 6 znaków, oczywiście może być alfa

  • Chciałbym ponownie wykorzystać istniejące, dobrze znane algos, jeśli są dostępne

Coś tam jest?

Kumar
źródło
Dlaczego nie użyć INT lub BIGINT, który jest automatycznie zwiększany? Jest prawdopodobnie najbardziej czytelny i może z łatwością obsługiwać głośność.
Malk
zgodnie z powyższym Q, starając się utrzymać maksymalnie 5/6 znaków i obsługiwać do 9999 nowych rekordów dziennie
Kumar
@Kumar - A jeśli potrzebujesz więcej niż 9999 rekordów w ciągu jednego dnia? Proponowane przez Ciebie rozwiązanie nie wydaje się możliwe do obrony.
ChaosPandion
@ChaosPandion: Myślę, że są to prawdopodobnie przybliżone przypuszczenia dotyczące obciążenia / ruchu, a nie twarde granice. Nie jestem pewien, dlaczego chcesz ustawić arbitralny limit liczby transakcji dziennie.
Paul Sasik
Możesz zakodować go do bazy 64 i użyć tego. Nie jestem pewien, czy mógłbyś zmniejszyć to mniejsze i nadal używać czytelnych znaków. Ale argumentowałbym, że podstawa 64 jest znacznie mniej czytelna niż podstawa 32, ponieważ wymaga dodania dodatkowego kwalifikatora do większości znaków (duże f, niższe o, niższe o w porównaniu z po prostu f, oo).
Malk

Odpowiedzi:

118

Base 62 jest używany przez tinyurl i bit.ly dla skróconych adresów URL. Jest to dobrze znana metoda tworzenia „unikalnych”, czytelnych dla człowieka identyfikatorów. Oczywiście będziesz musiał przechowywać utworzone identyfikatory i sprawdzać duplikaty podczas tworzenia, aby zapewnić ich niepowtarzalność. (Zobacz kod na dole odpowiedzi)

Metryki niepowtarzalności Base 62

5 znaków w bazie 62 daje 62 ^ 5 unikalnych identyfikatorów = 916 132 832 (~ 1 miliard) Przy 10 tys. Identyfikatorów dziennie będzie dobrze przez ponad 91 tys. Dni

6 znaków w bazie 62 daje 62 ^ 6 unikalnych identyfikatorów = 56800235584 (ponad 56 miliardów) Przy 10 tys. Identyfikatorów dziennie będziesz w porządku przez ponad 5 milionów dni

Podstawowe wskaźniki niepowtarzalności 36

6 znaków zapewni 36 ^ 6 unikalnych identyfikatorów = 2,176,782,336 (ponad 2 miliardy)

7 znaków zapewni 36 ^ 7 unikalnych identyfikatorów = 78,364,164,096 (ponad 78 miliardów)

Kod:

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();
    }
}

Wynik:

z5KyMg
wd4SUp
uSzQtH
UPrGAT
UIf2IS

QCF9GNM5
0UV3TFSS
3MG91VKP
7NTRF10T
AJK3AJU7
Paul Sasik
źródło
3
wygląda fantastycznie, coś, co nie ma znaczenia wielkości liter?
Kumar
2
Jeśli chcesz uniknąć rozróżniania wielkości liter, możesz użyć podstawy 36: codeproject.com/Articles/10619/Base-36-type-for-NET-C, ale aby uzyskać tyle permutacji jako podstawę 62, musisz użyć więcej znaków w swoim ID. To kompromis. Lub możesz spróbować użyć innych znaków oprócz alfy, ale to robi się brzydkie dla użytkowników.
Paul Sasik
2
tutaj stackoverflow.com/questions/9543892/ ... i wielkie dzięki
Kumar
11
Jedna myśl. Być może usuń samogłoski, aby zapobiec przypadkowemu wygenerowaniu przekleństw. Zwłaszcza jeśli jest to publiczne.
Damien Sawyer
4
W zależności od tego, gdzie go używasz (szczególnie jeśli oczekuje się, że ludzie będą czytać i ponownie wprowadzać kody), możesz rozważyć usunięcie z rozważań często mylonych znaków: 0 / O i I / l / 1. W niektórych przypadkach można to złagodzić przez dobry wybór czcionki, ale nie mogę powiedzieć na podstawie pytania, czy OP będzie nad tym kontrolę.
GrandOpener
17

Polecam http://hashids.org/, który konwertuje dowolną liczbę (np. DB ID) na łańcuch (używając soli).

Pozwala dekodować ten ciąg z powrotem do numeru. Nie musisz więc przechowywać go w bazie danych.

Posiada biblioteki dla JavaScript, Ruby, Python, Java, Scala, PHP, Perl, Swift, Clojure, Objective-C, C, C ++ 11, Go, Erlang, Lua, Elixir, ColdFusion, Groovy, Kotlin, Nim, VBA, CoffeeScript oraz Node.js i .NET.

Sława
źródło
1
Czy możesz podać inne opcje podobne do Twojej propozycji? - - To jest bardzo interesujące. Chciałbym wiedzieć, czy w PostgreSQL są jakieś domyślne opcje, takie jak ta.
Léo Léopold Hertz 준영
1
Oto wersja .NET tego programu, ale czy możesz wyjaśnić, jak to działa bez konieczności przechowywania go w bazie danych? Czy mogę generować tylko unikalne losy bez podawania liczb jako danych wejściowych i bez soli?
shaijut
@Slawa Potrzebuję czegoś w rodzaju hashidów dla .NET, ale ostateczny hash będzie przechowywany w bazie danych w kolumnie o stałej długości, czy można powiedzieć, że zawsze generuj hash o maksymalnej długości N?
Anon Dev,
6

Miałem podobne wymagania jak OP. Sprawdziłem dostępne biblioteki, ale większość z nich opiera się na przypadkowości i tego nie chciałem. Naprawdę nie mogłem znaleźć niczego, co nie byłoby oparte na przypadkowości i wciąż było bardzo krótkie ... Więc skończyłem na swojej własnej, opartej na technice Flickr , ale zmodyfikowanej tak, aby wymagała mniejszej koordynacji i pozwalała na dłuższe okresy offline.

W skrócie:

  • Centralny serwer wydaje bloki ID składające się z 32 identyfikatorów każdy
  • Lokalny generator identyfikatorów utrzymuje pulę bloków identyfikatorów w celu generowania identyfikatora za każdym razem, gdy jest on wymagany. Kiedy pula się wyczerpie, pobiera więcej bloków ID z serwera, aby ją ponownie zapełnić.

Niedogodności:

  • Wymaga centralnej koordynacji
  • Identyfikatory są mniej lub bardziej przewidywalne (mniej niż zwykłe identyfikatory DB, ale nie są losowe)

Zalety

  • Pozostaje w granicach 53 bitów (maksymalny rozmiar Javascript / PHP dla liczb całkowitych)
  • bardzo krótkie identyfikatory
  • Base 36 zakodowany tak, że jest bardzo łatwy do czytania, pisania i wymowy dla ludzi
  • Identyfikatory mogą być generowane lokalnie przez bardzo długi czas, zanim będzie trzeba ponownie skontaktować się z serwerem (w zależności od ustawień puli)
  • Teoretycznie nie ma szans na kolizje

Opublikowałem zarówno bibliotekę Javascript po stronie klienta, jak i implementację serwera Java EE. Wdrożenie serwerów w innych językach również powinno być łatwe.

Oto projekty:

suid - Identyfikatory unikalnych usług rozproszonych, które są krótkie i przyjemne

suid-server-java - implementacja serwera Suid dla stosu technologii Java EE.

Obie biblioteki są dostępne na liberalnej licencji open source Creative Commons. Mając nadzieję, że może to pomóc komuś innemu szukającemu krótkich unikalnych identyfikatorów.

Stijn de Witt
źródło
Czy możesz porównać stackoverflow.com/a/29372036/54964 ze swoją propozycją suid?
Léo Léopold Hertz 준영
1
Opiera się na liczbach losowych. Właściwie to jest całkiem niezłe. Ale twoje dokumenty nie będą tak krótkie, jak to tylko możliwe. Napisałem SUID, aby zacząć numerację od 1, więc zaczniesz od bardzo krótkich identyfikatorów. Pomyśl 3 lub 4 znaki. Poza tym, oprócz rozpoczynania od naprawdę krótkich, posiadanie (z grubsza) przyrostowo uporządkowanych identyfikatorów ma kilka innych zalet.
Stijn de Witt
3

Użyłem podstawy 36, kiedy rozwiązałem ten problem dla aplikacji, którą tworzyłem kilka lat temu. Musiałem wygenerować czytelny dla człowieka, racjonalnie unikalny numer (w każdym razie w bieżącym roku kalendarzowym). Zdecydowałem się użyć czasu w milisekundach od północy 1 stycznia bieżącego roku (więc każdego roku znaczniki czasu mogą się zduplikować) i przekonwertować go na podstawową liczbę 36. Jeśli opracowywany system napotkał błąd krytyczny, generował podstawowy numer 36 (7 znaków), który był wyświetlany użytkownikowi końcowemu za pośrednictwem interfejsu internetowego, który mógł następnie przekazać napotkany problem (i numer) do pracownika pomocy technicznej (który może następnie użyć go do znalezienia punktu w dziennikach, w którym rozpoczął się ślad stosu). Liczba taka jak 56af42g7jest nieskończenie łatwiejsze do odczytania i przekazania przez użytkownika niż znacznik czasu, taki jak 2016-01-21T15: 34: 29.933-08: 00 lub losowy identyfikator UUID, taki jak 5f0d3e0c-da96-11e5-b5d2-0a1d41d68578 .

Warren Smith
źródło
4
Czy możesz podać pseudokod w ustrukturyzowanej formie na temat swojej propozycji? To brzmi interesująco.
Léo Léopold Hertz 준영
0

Bardzo podoba mi się prostota kodowania GUID przy użyciu formatu Base64 i obcięcia końcowego == w celu uzyskania ciągu 22 znaków (zajmuje to jedną linię kodu i zawsze można przekonwertować go z powrotem na GUID). Niestety czasami zawiera znaki + i /. OK dla bazy danych, niezbyt dobre dla adresów URL, ale pomogło mi to docenić inne odpowiedzi :-)

Z https://www.codeproject.com/Tips/1236704/Reducing-the-string-Length-of-a-Guid autorstwa Christiaana van Bergena

Okazało się, że konwersja Guid (16 bajtów) do reprezentacji ASCII przy użyciu Base64 dała użyteczny i wciąż unikatowy identyfikator wiadomości składający się z zaledwie 22 znaków.

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

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

Na przykład: Guid 'e6248889-2a12-405a-b06d-9695b82c0a9c' (długość ciągu: 36) otrzyma reprezentację Base64: 'iYgk5hIqWkCwbZaVuCwKnA ==' (długość ciągu: 24)

Reprezentacja Base64 kończy się znakami „==”. Możesz je po prostu skrócić, bez żadnego wpływu na wyjątkowość. Pozostawiając Ci identyfikator składający się z zaledwie 22 znaków.

Ekus
źródło