TGViewer
EvApps EvApps @evapps_team · 198 subscribers
Post #1525 97
🐌 Почему UUID в первичном ключе тихо душит базу
UUID как id — вроде идеально: генерится на клиенте, не угадать, не конфликтует между сервисами
Все привыкли лепить его как первичный ключ и не думать
А потом база на паре миллионов строк начинает необъяснимо тупить на вставках
Разберём, почему так и что с этим делать

🎲 Корень зла — случайность Обычный UUID (v4) полностью случайный
Выглядит безобидно, но вот в чём засада: первичный ключ в базе хранится не кучей, а в отсортированном виде (B-tree индекс)
Когда id идёт по порядку (обычный автоинкремент 1, 2, 3...), каждая новая строка ложится в конец

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

💾 Мимо кэша Второй удар — по кэшу.

База держит в оперативке горячие страницы (buffer pool)
Когда вставки идут по порядку, ты всё время работаешь с одним и тем же «хвостом» индекса, он всегда в памяти
А со случайным UUID каждая вставка дёргает случайную страницу — сегодня эту, через миг вон ту
В память всё не влезает, и база начинает гонять страницы с диска
На больших таблицах это разница между «мгновенно» и «чего оно висит»

📏 И просто жирнее UUID — это 16 байт, а bigint — 8
Вроде мелочь, но первичный ключ тащится во все индексы и во все внешние ключи, что на него ссылаются Помножь на миллионы строк и десяток ссылающихся таблиц — набегает ощутимо
А если ты ещё и хранишь UUID строкой (varchar(36)) вместо родного типа — это уже 36+ байт, и вообще боль
Так делать не надо.

🛠 Что делать Хорошая новость: отказываться от UUID не нужно, надо взять правильный
UUIDv7 — это UUID, у которого в начале зашито время создания
Он всё так же уникален и генерится на клиенте, но при этом растёт по порядку — новые id всегда больше старых Для базы он ложится так же аккуратно, как автоинкремент: в конец, без разрывов страниц и без прыжков по памяти
Забираешь все плюсы UUID и убираешь главный минус
-- вместо случайного uuid_generate_v4()
-- берём time-ordered v7 (в свежих версиях уже из коробки)

Если UUIDv7 под рукой нет — есть ULID, работает по тому же принципу (время + случайность, растёт по порядку)
А если тебе вообще не нужна генерация на клиенте и распределённость — старый добрый bigint-автоинкремент по скорости не переплюнуть
И маленькое, но важное: храни UUID родным типом (uuid в постгресе, binary(16) в mysql), а не строкой
Иначе ты сверху к случайности добавляешь ещё и лишний вес

⚖️ Когда UUID реально оправдан
Чтобы не подумали, что UUID — это плохо
Он отлично заходит, когда id нужно сгенерить ДО похода в базу: например, клиент создаёт запись офлайн, или несколько сервисов пишут в одну таблицу и не должны драться за общий счётчик
Ещё UUID не даёт угадать соседние записи по id (с автоинкрементом любой видит, что до него было /order/1041, а значит есть и /order/1040)
Вопрос не в том, брать UUID или нет, а в том, чтобы он был упорядоченный, а не случайный

А вы уже переехали на v7? 🤔

#database #postgres #backend #performance #sql #dev
  • 👍 2
More from @evapps_team
  1. Sep 21, 2026🧩 Что тут не так? Код-загадка Формат новый - показываю код, ты угадываешь подвох, ниже ра…
  2. Sep 18, 2026🎭 Мифы про производительность, в которые верят даже опытные Миф 1: "Меньше строк кода - б…
  3. Sep 16, 2026🚨 Как перевод денег уронил нам прод ⏰ 19:10 Задеплоили долгожданное - переводы между коше…
  4. Sep 14, 2026🗃 Кэш поставили, а он отдаёт старьё В программировании две сложные вещи - инвалидация кэш…
  5. Sep 11, 2026🔌 "Too many connections" - и почему база падает под нагрузкой Под нагрузкой прилетает FAT…
  6. Sep 11, 2026Пока вы наслаждаетесь пятницей, мы напоминаем, что уже завтра стартует одна из наших любим…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →