Получить доступ

Как генерировать уникальные идентификаторы в распределённой системе?

MiddleЗадачи на проектирование
Ещё спрашивают так:
  • Как устроен Snowflake ID?
  • UUID или автоинкремент?
  • Почему UUIDv4 плох как первичный ключ?
Коротко

Варианты: автоинкремент (просто, но одна точка и угадываемость), UUIDv4 (без координации, но случайный — плохо для B-дерева), UUIDv7 / Snowflake (время в старших битах — почти упорядочены, без координации).

  • Snowflake: 41 бит времени + 10 бит узла + 12 бит счётчика = 64 бита, 4096 id в мс на узел
  • UUIDv7: 48 бит времени в мс + случайность, 128 бит, стандарт RFC 9562
  • Выдача диапазонов из базы — компромисс для автоинкремента

А ещё Риски Snowflake: откат часов и уникальность номера узла

Так подсказку видит пользователь sotto на живом собеседовании — за пару секунд, пока вы думаете вслух.

Подробно

Требования: уникальность без центральной точки на каждую вставку, высокая скорость, желательно — сортировка по времени создания и компактность, иногда — неугадываемость.

Автоинкремент базы. Просто, компактно, упорядоченно. Минусы: при шардировании нужны разные диапазоны или шаги (auto_increment_offset), генерация привязана к базе (id известен только после вставки), id раскрывают объём бизнеса и позволяют перебирать ресурсы.

Выдача диапазонов. Сервис берёт у базы блок из 1000 номеров (UPDATE seq SET value = value + 1000 RETURNING value) и выдаёт из памяти. Быстро, координация редкая; при падении теряется хвост блока (дырки в последовательности — нормально).

UUIDv4 — 122 случайных бита, генерируется где угодно без координации, вероятность коллизии пренебрежимо мала. Минусы: 16 байт; случайный порядок — каждая вставка в B-дерево индекса попадает в случайную страницу: больше разделений страниц, хуже кэш, больше WAL. На больших таблицах вставка заметно медленнее последовательного ключа.

Упорядоченные по времени:

  • Snowflake (Twitter): 64 бита = знак + 41 бит миллисекунд от своей эпохи (~69 лет) + 10 бит номера узла (1024 узла) + 12 бит счётчика в пределах миллисекунды (4096 id). Помещается в bigint, сортируется по времени. Нужно: уникально раздать номера узлов (конфиг, ZooKeeper/etcd, аренда), обработать откат часов (NTP) — ждать или отказывать, если время пошло назад, и переполнение счётчика в миллисекунде — ждать следующую;
  • UUIDv7 (RFC 9562, 2024) — 48 бит времени в миллисекундах + версия + случайные биты. Без координации и номеров узлов, почти монотонен, хорош для индексов; поддерживается в PostgreSQL 18 (uuidv7()) и библиотеках;
  • ULID, KSUID — похожие идеи с текстовым представлением.

Неугадываемость. Упорядоченные id раскрывают время создания и плотность. Для публичных ссылок — отдельный случайный публичный токен или обфускация, а не опора на секретность id.

Как выбрать: одна база и внутренние id — автоинкремент или bigint identity; распределённая генерация и индексы — UUIDv7 или Snowflake; нужен короткий публичный код — base62 от числа с перемешиванием.

Пример

import time, threading

class Snowflake:
    EPOCH = 1_704_067_200_000          # 2024-01-01 в мс
    def __init__(self, node):
        assert 0 <= node < 1024
        self.node, self.seq, self.last, self.lock = node, 0, -1, threading.Lock()

    def next(self):
        with self.lock:
            now = int(time.time() * 1000)
            if now < self.last:
                raise RuntimeError("часы пошли назад")
            if now == self.last:
                self.seq = (self.seq + 1) & 0xFFF
                if self.seq == 0:                    # 4096 id в этой мс — ждём следующую
                    while now <= self.last: now = int(time.time() * 1000)
            else:
                self.seq = 0
            self.last = now
            return ((now - self.EPOCH) << 22) | (self.node << 12) | self.seq

Что хотят услышать

  • Варианты и их компромиссы: автоинкремент, диапазоны, UUIDv4, Snowflake, UUIDv7.
  • Почему случайный UUID плох для B-дерева.
  • Устройство Snowflake и его риски: часы, номер узла.
  • Вопрос угадываемости публичных id.

Частые ошибки

  • Брать UUIDv4 первичным ключом огромной таблицы и не понимать, почему вставка медленная.
  • Генерировать id из времени без счётчика и номера узла.

Связанные вопросы

Закрытая бета

Подсказки на собеседовании — в реальном времени

sotto слышит вопрос интервьюера и показывает короткий ответ и план, как в блоке «Коротко». Оставьте контакт — пришлём доступ и промокод.

  • Промокод на 2 часа — сверх пробных 30 минут
  • Программа для Windows — скачать сразу, войти — через Яндекс ID или VK ID
  • Прямая связь с разработчиком: что улучшить — сделаем