Генерация ID в распределённой системе, часть 1



Задача считается сеньорной и входит в категорию system design. Вариантов решения много, и выбирать нужно с умом. Чтобы примерно сориентироваться, обозначу основные варианты и опорные точки.



Над чем подумать в самом начале:



1️⃣ Насколько уникальным должен быть ID?

🔸 В рамках одного сервиса

🔸 Уникальным в пределах системы в течение какого-то времени

🔸 Глобально уникальным в течение всей жизни системы



Два последних варианта влияют на длину id. Чем он короче, тем скорее наступит переполнение. Чем длиннее — тем больше памяти займёт id



2️⃣ Как часто нужно генерировать id?



Влияет на размер и немного на реализацию



3️⃣ Если сущность отдаётся за пределы системы, что видит внешний пользователь?



▫️ id как есть: /user/123

▫️ Декодированный id через Base64: /user/MTIz

▫️ Зашифрованный id: /user/67FA78



4️⃣ Формат



🔸 Возрастающая последовательность



Глобальный счётчик, последовательность в БД или местный AtomicLong



🔸 Случайный набор цифр



Глобально уникальный UUID или локальный Random

Самый быстрый вариант



🔸 Вариации Snowflake



Формат Snowflake придумали в Twitter. В оригинале id формируется как комбинация



timestamp + machine_id + sequence_id

(значения складываются как строки, а не как числа)



❄️ timestamp — количество миллисекунд

❄️ machine_id — id сервера

❄️ sequence_id — возрастающая последовательность



id содержит что-то полезное

Можно сортировать по полям, входящим в id

Глобальная уникальность



Machine id часто меняют на пару (id рабочей машины + id процесса) или (id датацентра + id сервера). Можно вдохновиться и составить свою комбинацию полей



Технические моменты Snowflake



Чтобы timestamp не получался слишком большим, отсчитывайте миллисекунды от какой-то даты отсчёта.



Machine id извлекается в начале работы сервиса

▫️ из распределённого счётчика. Например, из Zookeeper

▫️ из конфига, если при развёртывании ведётся счётчик



Для возрастающей последовательности подойдёт локальный AtomicLong или sequence в БД.



Генерация ID в базе данных



Часто говорят, что генерация id через БД — плохое решение. Все сущности должны проходить через один экземпляр БД, чтобы не было дубликатов, и это ограничивает масштабируемость.



В следующем посте расскажу, как решить эту проблему:)



Генерация через БД подходит, если объект и так сохраняется в базе данных. Если взаимодействий с БД нет (например, нужно id сообщения для кафки), конечно, нужны другие решения.



Как формировать Snowflake id на основе sequence в БД?



Шок контент: в хранимой процедуре. Формирование id — технический момент, а не бизнес-логика. Поэтому такое решение ок.



Как формируется UUID, может ли он повторяться?



Стандарт UUID описывает 5 стратегий генерации UUID. Метод UUID.randomUUID() использует Version 4, генерацию с помощью случайных чисел. Я тоже не доверяю случайным числам, но формулы обещают, что всё будет ок