company_banner

Как генерируются UUID

Автор оригинала: Aryaman Sharda
  • Перевод

Вы наверняка уже использовали в своих проектах UUID и полагали, что они уникальны. Давайте рассмотрим основные аспекты реализации и разберёмся, почему UUID практически уникальны, поскольку существует мизерная возможность возникновения одинаковых значений.

Современную реализацию UUID можно проследить до RFC 4122, в котором описано пять разных подходов к генерированию этих идентификаторов. Мы рассмотрим каждый из них и пройдёмся по реализации версии 1 и версии 4.

Теория


UUID (universally unique IDentifier) — это 128-битное число, которое в разработке ПО используется в качестве уникального идентификатора элементов. Его классическое текстовое представление является серией из 32 шестнадцатеричных символов, разделённых дефисами на пять групп по схеме 8-4-4-4-12.

Например:

3422b448-2460-4fd2-9183-8000de6f8343

Информация о реализации UUID встроена в эту, казалось бы, случайную последовательность символов:

xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

Значения на позициях M и N определяют соответственно версию и вариант UUID.

Версия


Номер версии определяется четырьмя старшими битами на позиции М. На сегодняшний день существуют такие версии:


Вариант


Это поле определяет шаблон информации, встроенной в UUID. Интерпретация всех остальных битов в UUID зависит от значения варианта.

Мы определяем его по первым 1-3 старшим битам на позиции N.


Сегодня чаще всего используют вариант 1, при котором MSB0 равняется 1, а MSB1 равняется 0. Это означает, что с учётом подстановочных знаков — битов, отмеченных х — единственными возможными значениями будут 8, 9, A или B.

Памятка:

1 0 0 0 = 8
1 0 0 1 = 9
1 0 1 0 = A
1 0 1 1 = B

Так что если вы видите UUID с такими значениями на позиции N, то это идентификатор в варианте 1.

Версия 1 (время + уникальный или случайный идентификатор хоста)


В этом случае UUID генерируется так: к текущему времени добавляется какое-то идентифицирующее свойство устройства, которое генерирует UUID, чаще всего это MAC-адрес (также известный как ID узла).

Идентификатор получают с помощью конкатенации 48-битного МАС-адреса, 60-битной временной метки, 14-битной «уникализированной» тактовой последовательности, а также 6 битов, зарезервированных под версию и вариант UUID.

Тактовая последовательность — это просто значение, инкрементируемое при каждом изменении часов.

Временная метка, которая используется в этой версии, представляет собой количество 100-наносекундных интервалов с 15 октября 1582 года — даты возникновения григорианского календаря.

Возможно, вы знакомы с принятым в Unix-системах исчислением времени с начала эпохи. Это просто другая разновидность Нулевого дня. В сети есть сервисы, которые помогут вам преобразовать одно временное представление в другое, так что не будем на этом останавливаться.

Хотя эта реализация выглядит достаточно простой и надёжной, однако использование MAC-адреса машины, на которой генерируется идентификатор, не позволяет считать этот метод универсальным. Особенно, когда главным критерием является безопасность. Поэтому в некоторых реализациях вместо идентификатора узла используется 6 случайных байтов, взятых из криптографически защищённого генератора случайных чисел.

Сборка UUID версии 1 происходит так:

  1. Берутся младшие 32 бита текущей временной метки UTC. Это будут первые 4 байта (8 шестнадцатеричных символов) UUID [TimeLow].
  2. Берутся средние 16 битов текущей временной метки UTC. Это будут следующие 2 байта (4 шестнадцатеричных символа) [TimeMid].
  3. Следующие 2 байта (4 шестнадцатеричных символа) конкатенируют 4 бита версии UUID с оставшимися 12 старшими битами текущей временной метки UTC (в которой всего 60 битов) [TimeHighAndVersion].
  4. Следующие 1-3 бита определяют вариант версии UUID. Оставшиеся биты содержат тактовую последовательность, которая вносит небольшую долю случайности в эту реализацию. Это позволяет избежать коллизий, когда в одной системе работает несколько UUID-генераторов: либо системные часы переводятся назад для генератора, либо изменение времени замедляется [ClockSequenceHiAndRes && ClockSequenceLow].
  5. Последние 6 байтов (12 шестнадцатеричных символов, 48 битов) — это «идентификатор узла», в роли которого обычно выступает MAC-адрес генерирующего устройства [NodeID].

UUID версии 1 генерируется с помощью конкатенации:

TimeLow + TimeMid + TimeHighAndVersion + (ClockSequenceHiAndRes && ClockSequenceLow) + NodeID 

Поскольку эта реализация зависит от часов, нам нужно обрабатывать пограничные ситуации. Во-первых, для минимизации коррелирования между системами по умолчанию тактовая последовательность берётся как случайное число — так делается лишь один раз за весь жизненный цикл системы. Это даёт нам дополнительное преимущество: поддержку идентификаторов узлов, которые можно переносить между системами, поскольку начальное значение тактовой последовательности совершенно не зависит от идентификатора узла.

Помните, что главная цель использования тактовой последовательности — внести долю случайности в наше уравнение. Биты тактовой последовательности помогают расширить временную метку и учитывать ситуации, когда несколько UUID генерируются ещё до того, как изменяются процессорные часы. Так мы избегаем создания одинаковых идентификаторов, когда часы переводятся назад (устройство выключено) или меняется идентификатор узла. Если часы переведены назад, или могли быть переведены назад (например, пока система была отключена), и UUID-генератор не может убедиться, что идентификаторы сгенерированы с более поздними временными метками по сравнению с заданным значением часов, тогда нужно изменить тактовую последовательность. Если нам известно её предыдущее значение, его можно просто увеличить; в противном случае его нужно задать случайным образом или с помощью высококачественного ГПСЧ.

Версия 2 (безопасность распределённой вычислительной среды)


Главное отличие этой версии от предыдущей в том, что вместо «случайности» в виде младших битов тактовой последовательности здесь используется идентификатор, характерный для системы. Часто это просто идентификатор текущего пользователя. Версия 2 используется реже, она очень мало отличается от версии 1, так что идём дальше.

Версия 3 (имя + MD5-хэш)


Если нужны уникальные идентификаторы для информации, связанной с именами или наименованием, то для этого обычно используют UUID версии 3 или версии 5.

Они кодируют любые «именуемые» сущности (сайты, DNS, простой текст и т.д.) в UUID-значение. Самое важное — для одного и того же namespace или текста будет сгенерирован такой же UUID.

Обратите внимание, что namespace сам по себе является UUID.

let namespace = “digitalbunker.dev”
let namespaceUUID = UUID3(.DNS, namespace)

// Ex: 
UUID3(namespaceUUID, “/category/things-you-should-know-1/”) 
4896c91b-9e61-3129-87b6-8aa299028058

UUID3(namespaceUUID, “/category/things-you-should-know-2/”) 
29be0ee3-fe77-331e-a1bf-9494ec18c0ba

UUID3(namespaceUUID, “/category/things-you-should-know-3/”) 
33b06619-1ee7-3db5-827d-0dc85df1f759

В этой реализации UUID namespace преобразуется в строку байтов, конкатенированных с входным именем, затем хэшируется с помощью MD5, и получается 128 битов для UUID. Затем мы переписываем некоторые биты, чтобы точно воспроизвести информацию о версии и варианте, а остальное оставляем нетронутым.

Важно понимать, что ни namespace, ни входное имя не могут быть вычислены на основе UUID. Это необратимая операция. Единственное исключение — брутфорс, когда одно из значений (namespace или текст) уже известно атакующему.

При одних и тех же входных данных генерируемые UUID версий 3 и 5 будут детерминированными.

Версия 4 (ГПСЧ)


Самая простая реализация.

6 битов зарезервированы под версию и вариант, остаётся ещё 122 бита. В этой версии просто генерируется 128 случайных битов, а потом 6 из них заменяется данными о версии и варианте.

Такие UUID полностью зависят от качества ГПСЧ (генератора псевдослучайных чисел). Если его алгоритм слишком прост, или ему не хватает начальных значений, то вероятность повторения идентификаторов возрастает.

В современных языках чаще всего используются UUID версии 4.

Её реализация достаточно простая:

  1. Генерируем 128 случайных битов.
  2. Переписываем некоторые биты корректной информацией о версии и варианте:

    1. Берём седьмой бит и выполняем c 0x0F операцию AND для очистки старшего полубайта. А затем выполняем с 0x40 операцию OR для назначения номера версии 4.
    2. Затем берём девятый байт, выполняем c 0x3F операцию AND и с 0x80 операцию OR.
  3. Преобразуем 128 битов в шестнадцатеричный вид и вставляем дефисы.

Версия 5 (имя + SHA-1-хэш)


Единственное отличие от версии 3 в том, что мы используем алгоритм хэширования SHA-1 вместо MD5. Эта версия предпочтительнее третьей (SHA-1 > MD5).

Практика


Одним из важных достоинств UUID является то, что их уникальность не зависит от центрального авторизующего органа или от координации между разными системами. Кто угодно может создать UUID с определённой уверенностью в том, что в обозримом будущем это значение больше никем не будет сгенерировано.

Это позволяет комбинировать в одной БД идентификаторы, созданные разными участниками, или перемещать идентификаторы между базами с ничтожной вероятностью коллизии.

UUID можно использовать в качестве первичных ключей в базах данных, в качестве уникальных имён загружаемых файлов, уникальных имён любых веб-источников. Для их генерирования вам не нужен центральный авторизующий орган. Но это обоюдоострое решение. Из-за отсутствия контролёра невозможно отслеживать сгенерированные UUID.

Есть и ещё несколько недостатков, которые нужно устранить. Неотъемлемая случайность повышает защищённость, однако она усложняет отладку. Кроме того, UUID может быть избыточным в некоторых ситуациях. Скажем, не имеет смысла использовать 128 битов для уникальной идентификации данных, общий размер которых меньше 128 битов.

Уникальность


Может показаться, что если у вас будет достаточно времени, то вы сможете повторить какое-то значение. Особенно в случае с версией 4. Но в реальности это не так. Если бы вы генерировали один миллиард UUID в секунду в течение 100 лет, то вероятность повторения одного из значений была бы около 50 %. Это с учётом того, что ГПСЧ обеспечивает достаточное количество энтропии (истинная случайность), иначе вероятность появления дубля будет выше. Более наглядный пример: если бы вы сгенерировали 10 триллионов UUID, то вероятность появления двух одинаковых значений равна 0,00000006 %.

А в случае с версией 1 часы обнулятся только в 3603 году. Так что если вы не планируете поддерживать работу своего сервиса ещё 1583 года, то вы в безопасности.

Впрочем, вероятность появления дубля остаётся, и в некоторых системах стараются это учитывать. Но в подавляющем большинстве случаев UUID можно считать полностью уникальными. Если вам нужно больше доказательств, вот простая визуализация вероятности коллизии на практике.
Mail.ru Group
Строим Интернет

Комментарии 34

    +1

    Спасибо за статью, было интересно!
    Расскажите про эти странные пресеты для UUID5, например DNS?
    P.S. теги читаю ))

      +4
      Теги то читаешь, а ярлычки к статьям нет)
      0
      Вы наверняка уже использовали в своих проектах UUID и полагали, что они уникальны.

      Я наверняка видел множество проектов, авторы которых использовали UUID и полагали, что они уникальны. И во всех этих системах возникали проблемы с дублированием UUID'ов.

        +5

        можно детали?

          0
          Из-за того что в UUID входит рандомная составляющая, его после генерации надо куда-то сохранить. Например, в файл. А файлы внезапно можно копировать. Ну и дальше при копировании забывают перегенерировать UUID, в результате получаем в системе две сущности с одинаковым UUID.
            0

            какую альтернативу вы видите?

              –2
              К примеру если это код, то рядом (а может и в заголовке на видном месте) с таким UUID ставим название программы и реальный UUID делаем через XOR с именем (самое примитивное). При копировании новый программист скорее всего поменяет имя программы.
                0

                Зачем xor, когда в стандарте описан способ генерации uuid из другого uuid и произвольной строки?

                  0
                  Так это же отлично.
                  Только не понятно, мне "-" поставили за идею или за примитивную реализацию? Если за реализацию, так я и не скрываю, что это примитивное «демо», а в реале по другому нужно.
          +1
          Звучит как «не вяжи меня начальник, я серийник на винду подобрал, долбил по клаве неделю»
            +6
            Дублирование обычно возникает, когда среди авторов появляется умный и начинает генерировать всякие 70726573-7469-6469-6769-746174696f6e или вообще 01234567-89ab-cdef-0123-456789abcdef руками.
              +3
              Тоже встречал. Но там генератор был самописный на php, в основе которого rand().
              Проблема была связана со слишком малой длиной числа.
              +10
              В статье, на мой взгляд, не хватает ответа на самый главный вопрос: а что собственно использовать?

              Мой ответ такой:

              Версии 1 и 2 (время + идентификатор хоста) в теории надежна, но с практической точки зрения опасна, поскольку создает два неявных требования: корректное течение системного времени и уникальность MAC адресов. Вы, как разботчик софта, эти факторы не контролируете. Только вопрос времени (или MAC адреса), когда одно из этих требований нарушится.

              Версии 3 и 5 (SHA-1 или MD5 хеш) неоправданно вычислительно дорогие, и при этом не надежные. Для обоих функций уже найдены коллизии. Это означает, что если на идентификтор, от которого берется хеш, влияет кто-то из вне (пользователи), ждите беды. Можно применять только в случаях, когда генерация UUID зависит только от доверенных данных (но все равно не стоит).

              Версия 4 (ГПСЧ). Самая быстрая и надежная, при корректном использовании. Корректное использование заключается в следующем:

              1. Используйте генератор псевдослучайных чисел полного цикла (xorshift или PCG).
              2. Убедитесь, что состояния вашего генератора – 128 бит.
              3. Убедитесь, что вы инициализурете ваш генератор истинно случайной энтропией (/dev/random, как минимум 128 бит).
              4. НЕ используйте только /dev/random как постоянный генератор UUID, кроме случаев, когда сама возможность угадать UUID должна быть невозможной.

              Если все сделать правильно, генерация UUID будет быстрой, надежной и зависеть только от вашего кода, и это хорошо (ну и еще от адекватности /dev/random).

              А еще, если у вас нет требования использовать именно стандарт UUID, то посмотрите в сторону nanoid, благо его поддержка уже реализована на 17 языках. Он в короче (21 байт вместо 36), надежнее (126 бит против 122), быстрее и гибче чем UUID.

              И да, при непрекращающейся генерации на скорости 1000 идентификаторов в секунду, понадобится больше 1000 лет, чтобы иметь 1% шанс хотя бы одной коллизии для случайных 16-символьных base32 идентификаторов вроде «GByvN46iRhqfJ1vi».

              Так что прекращайте захламлять память своими {3422b448-2460-4fd2-9183-8000de6f8343}.
                0
                Вот это ОТВЕТ!
                  +1
                  > А еще, если у вас нет требования использовать именно стандарт UUID, то посмотрите в сторону nanoid, благо его поддержка уже реализована на 17 языках. Он в короче (21 байт вместо 36), надежнее (126 бит против 122), быстрее и гибче чем UUID.

                  Вот тут я споткнулся. Как при сравнимом порядке числа значимых бит он может короче? Как можно сжать рандомное число, у которого энтропия не даст сжать его сильнее, чем оно есть на самом деле?

                  И ещё вопрос: как хранить nanoid, например, в постгресе или в любой иной другой базе данных? Для uuid там нативная поддержка есть, а как быть с этим стандартом?

                  UPD: А, дошло. Имелась в виду длина строкового представления. Nanoid просто используют base64(или кастомный алфавит, что здорово), в отличие от uuid. Но в БД их всё равно придётся хранить как числовое поле по 16 байт, без магии.
                    +1
                    Вот тут я споткнулся. Как при сравнимом порядке числа значимых бит он может короче?

                    Элементарно же, base64 вместо hex и отсутствие разделителей. По крайней мере, лично я стал бы задачу решать именно так. Разумеется, экономия достигается только в строковой форме, в форме же массива байт в обоих случаях их будет 16 штук.


                    Хранить в БД можно прямо в поле для UUID :-)

                    +1
                    Версии 1 и 2 (время + идентификатор хоста) в теории надежна, но с практической точки зрения опасна, поскольку создает два неявных требования

                    ещё они могут приводить к нежелательной деанонимизации (напоминает как в ipv6-адрес сначала включили mac, а потом удаляли).


                    ИМХО вообще версии UUID стоит изучать только ради общего образования, на практике отличное от 4 не используется (а где используется — зря)


                    А еще, если у вас нет требования использовать именно стандарт UUID, то посмотрите в сторону nanoid, благо его поддержка уже реализована на 17 языках. Он в короче (21 байт вместо 36), надежнее (126 бит против 122), быстрее и гибче чем UUID.

                    всё-таки UUID — мейнстрим. он и в БД бинарно хранится, и в куче протоколов уже используется, …

                      0
                      на практике отличное от 4 не используется (а где используется — зря)

                      хотя могу быть неправ, может быть в каких-то задачах требуется воcпроизводимый UUID, просто мне такие задачи не встречались.

                        0
                        воcпроизводимый UUID

                        Оксюморон?
                      0
                      Это несколько не так.

                      Версии 1 и 2 имеют дополнительное поле Clock sequence, которое защищает от ненадёжного течения времени. Оно заполняется рандомом либо отслеживается в системе и инкрементируется, когда часы сдвигаются назад.
                      Что до ненадёжности MAC, то даже от этого в принципе есть кое-какое средство: купить отдельную сетевую карту от приличного производителя, записать её MAC, уничтожить её физически, генерить UUID с записанным MAC и за пределы генерящей системы его не сообщать. Конечно, затратно. Но с таким MAC, нормальными часами и Clock sequence версия 1 даёт хотя бы призрачный шанс сделать UUID действительно 100% уникальным для тех, кому это по странному ТЗ действительно надо.

                      Что касается версий 3 и 5, они, строго говоря, не для ввода UUID от пользователей или внешних источников. Они годны лишь для специальных сценариев – есть, представим, стандартная библиотека .NET, и надо каждому классу в ней назначить UUID. Причём чтоб он не менялся между версиями и правками. Руками создавать их и вставлять в код муторно, есть немаленький шанс ошибки copy+paste. Но можно из имён классов (вместе с namespace) их сгенерить именно в виде хеша. Хеш даст повторяемость. Использование стандартного UUID позволит их смешивать с редкими назначенными вручную (уже версии 4). Вероятность коллизий невелика, и от них даже можно защититься. Криптографически стойкая необратимость тут даром никому не нужна.

                      Так что на разные задачи подойдут разные версии. На типовой уникальный идентификатор – да, версия 4 лучше всего, качественный генератор случайных чисел – обязателен.
                        –1
                        Версии 1 и 2 имеют дополнительное поле Clock sequence, которое защищает от ненадёжного течения времени.
                        Насколько защищают? Гарантированно защищают или «чуть-чуть» защищают? Можете предоставить качественное сравнение, скажем с UUID4?
                        купить отдельную сетевую карту от приличного производителя, записать её MAC, уничтожить её физически
                        Чем данный ритуал принципиально отличается от случайной генерации MAC, кроме того, что полагается на благоразумность производителя, требует финансовых затрат и бесмысленных усилий?

                        Каким образом такой подход защищает от ситуации, когда админ скопирует MAC системы, на которой выполняется генерация (или клонирует виртуальную машину)?
                        даёт хотя бы призрачный шанс сделать UUID действительно 100% уникальным для тех, кому это по странному ТЗ действительно надо
                        Меня, как архитектора, не устраивает «дает призначный шанс» от слова совсем.
                        Но можно из имён классов (вместе с namespace) их сгенерить именно в виде хеша.
                        UUID – universally unique identifier. Это значит, что он должен быть глобально уникальным, а не только в рамках некоторой системы. Хеш, взятый от простой строки, нельзя считать уникальным. Как планируете гарантировать глобальную уникальность?
                        Хеш даст повторяемость.
                        Подскажите, а зачем вам воспроизводимость идентификаторов? Точнее, зачем вам идентификаторы, которые необходимо воспроизводить? Можно ли значения, которые необходимо воспроизводить, называть идентификаторами?
                        Вероятность коллизий невелика
                        Какая?
                        и от них даже можно защититься
                        Если вы защищаетесь от коллизии идентификаторов, значит ваши идентификаторы – не идентификаторы, и в действительности ваши идентификаторы – это что-то другое. Тут уж выбирайте, либо «защита от коллизии UUID», либо «чтоб он не менялся между версиями и правками» (переименование класса – тоже правка).
                        Так что на разные задачи подойдут разные версии.
                        Только если задача поставлена как «использовать такую-то версию UUID». Во всех остальных случаях использование 4й версии наиболее оправдано. Хотя бы потому, что ее свойства поддаются количественным оценкам.
                          0
                          Версии 1 и 2 имеют дополнительное поле Clock sequence, которое защищает от ненадёжного течения времени.
                          Насколько защищают? Гарантированно защищают или «чуть-чуть» защищают? Можете предоставить качественное сравнение, скажем с UUID4?
                          Чем данный ритуал принципиально отличается от случайной генерации MAC, кроме того, что полагается на благоразумность производителя, требует финансовых затрат и бесмысленных усилий?

                          Каким образом такой подход защищает от ситуации, когда админ скопирует MAC системы, на которой выполняется генерация (или клонирует виртуальную машину)?
                          Смысл ритуала в том, чтобы завести систему, в памяти которой MAC, которого больше нигде нет. Вариант «админ скопирует» нужно давить организационно, для этого и условие «за пределы системы его не сообщать».

                          Если при этом в системе ещё и отследить любые переводы часов назад, и на каждом такому переводе увеличивать счётчик, помещаемый в Clock sequence, то получается UUID, которые действительно 100% не будут повторяться, пока счётчик не переполнится. В Clock sequence 14 бит.

                          Разумеется, это затратная штука. И, очевидно, уязвимая на практике. Простому разрабу обычно ничего этого не нужно. Но… жизнь сложная и разнообразная, и иногда попадаются, скажем так, особенные заказчики и ситуации. Поэтому, по-моему, не стоит забывать про такое использование версии 1, хотя бы чтобы не изобретать велосипеды, если вдруг.

                          Подскажите, а зачем вам воспроизводимость идентификаторов? Точнее, зачем вам идентификаторы, которые необходимо воспроизводить? Можно ли значения, которые необходимо воспроизводить, называть идентификаторами?
                          Зачем? Чтобы поместить в общее пространство идентификаторов UUID нечто частное, что обычно идентифицируется строками, так что у людей в сознании есть только строки. UUIDы при этом зачем-то технологически нужны, но получается так, что map строк в UUID некому отслеживать. А новые строки время от времени появляются.
                          UUID – universally unique identifier. Это значит, что он должен быть глобально уникальным, а не только в рамках некоторой системы. Хеш, взятый от простой строки, нельзя считать уникальным. Как планируете гарантировать глобальную уникальность?
                          Версия 4 не гарантирует глобальной уникальности, а даёт её только с некоторой вероятностью. Это как-то никому не мешает.
                          Вероятность коллизий невелика

                          Какая?
                          Обычная для хеша. Раз используется 122 бита от хеша, значит, 2^-61. Это хуже, чем версия 4, и обычно лучше к этому не прибегать. Это костыль, он несколько портит глобальную уникальность, но за счёт него можно состыковать сценарии и данные, с которыми иначе не получается.

                          И да, пока в тупик никто не загнал, пользуйтесь только версией 4. Я с этим не спорю, только хочу заметить, что у других есть свои, очень узкие и ненормальные сферы применения. Количественные оценки у всех версий есть, но с учётом затрат у версии 4 они почти всегда лучше.
                      +3

                      А в чём прикол совершенно разные алгоритмы, обладающие совершенно разными характеристиками называть одним именем? Да ещё и тратить драгоценные биты на указание версии.

                        +1
                        Итоговый формат полностью одинаковый. Видимо для удобства
                      • НЛО прилетело и опубликовало эту надпись здесь
                          0
                          А вот потомкам в 3603 году придётся разгребать! А если при нас достигнут существенного продления жизни, то и нам самим.
                          –3
                          Я все равно так и не понял, что мешает дважды сделать Random(2^64-1), чтобы Random основывался на текущем показании системных часов.
                            0

                            В принципе ничего не мешает: вы получите 128 случайных бит )

                              0
                              Ну вот, и уникальность обеспечивается равномерным распределением вероятности по всему диапазону, разве нет?
                                0

                                Вот с распределением бит тут хорошо бы эксперта спросить :) вполне возможно что там не так всё рандомно, как хотелось бы)

                            0

                            А я генерировал uuid просто рандомом, не задавал биты версии. Надеюсь негативных последствий не будет?..

                              +1

                              Умрешь!

                                0

                                Вот блин..




                                А если серьезно?

                                  +1

                                  В теории возможны ошибки валидации при передачи таких id во внешние системы, но я с таким не встречался.
                                  Postgres например эти биты не проверяет.

                            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                            Самое читаемое