> Какой тип ID является последовательным? (Go)

Уровень: middle · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: InDrive

Стек: Go

> Пример ответа

Короткий ответ

Последовательным типом ID в PostgreSQL является SERIAL (или его варианты BIGSERIAL, SMALLSERIAL), а также IDENTITY-колонки (GENERATED ALWAYS AS IDENTITY / GENERATED BY DEFAULT AS IDENTITY). В Go-приложениях обычно используется SERIAL или BIGSERIAL для первичных ключей, так как они автоматически инкрементируются при вставке строки.

Подробное объяснение

SERIAL - это не настоящий тип данных, а синтаксический сахар, который создаёт целочисленную колонку с последовательностью (sequence). При создании таблицы:

SQL
CREATE TABLE users (
id SERIAL PRIMARY KEY
);

PostgreSQL автоматически создаёт sequence, привязывает её к колонке и подставляет следующее значение при вставке, если значение не указано явно.

В отличие от SERIAL, стандартный IDENTITY (появился в PostgreSQL 10) является частью SQL-стандарта и имеет два варианта:

  • GENERATED ALWAYS - запрещает явную вставку значения (только через OVERRIDING SYSTEM VALUE)
  • GENERATED BY DEFAULT - позволяет вставлять свои значения, если это необходимо

Для Go-разработчика разница не критична, так как драйверы (pgx, database/sql) одинаково работают с обоими вариантами. Однако IDENTITY предпочтительнее с точки зрения стандартизации и явного управления.

На практике

В Go-приложениях с PostgreSQL обычно используют BIGSERIAL для больших таблиц, чтобы избежать переполнения INT (максимум ~2.1 млрд). При работе с ORM (например, GORM) достаточно указать тег:

GO
type User struct {
ID uint `gorm:"primaryKey"`
}

GORM автоматически создаст колонку с serial-типом при миграции.

Для вставки и получения сгенерированного ID в Go используется RETURNING id:

GO
var id int64
err := db.QueryRow(
"INSERT INTO users (name) VALUES ($1) RETURNING id",
"Alice",
).Scan(&id)

Пример кода

SQL
-- Вариант с SERIAL
CREATE TABLE orders (
id BIGSERIAL PRIMARY KEY,
total NUMERIC(10,2) NOT NULL
);
-- Вариант с IDENTITY (рекомендуемый)
CREATE TABLE products (
id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
name TEXT NOT NULL
);
GO
// Вставка с получением ID
func createUser(ctx context.Context, db *sql.DB, name string) (int64, error) {
var id int64
err := db.QueryRowContext(ctx,
`INSERT INTO users (name) VALUES ($1) RETURNING id`,
name,
).Scan(&id)
return id, err
}

Как отвечать на собеседовании

Начните с прямого ответа: SERIAL или IDENTITY. Затем кратко объясните механизм работы (sequence). Упомяните разницу между SERIAL и IDENTITY, если интервьюер спросит. Покажите понимание практических аспектов: как получить сгенерированный ID в Go, какие подводные камни есть (например, currval vs RETURNING). Если спросят про альтернативы (UUID), объясните trade-off: последовательные ID компактны и индексируются быстрее, но раскрывают количество записей.

Что проверяет интервьюер

  • Понимание того, что SERIAL - это не тип данных, а обёртка над sequence
  • Знание стандартного IDENTITY и его преимуществ
  • Умение работать с RETURNING для получения сгенерированного ключа
  • Понимание разницы между SERIAL и UUID в контексте производительности и безопасности
  • Осведомлённость о проблемах с конкурентной вставкой (sequence не гарантирует отсутствие дырок)

Типичные ошибки

  • Утверждение, что SERIAL - это отдельный тип данных в PostgreSQL
  • Использование currval вместо RETURNING - это лишний запрос и потенциальная гонка при конкурентных вставках
  • Игнорирование BIGSERIAL для таблиц с потенциально большим количеством строк
  • Путаница между GENERATED ALWAYS и GENERATED BY DEFAULT - в первом случае явная вставка значения вызовет ошибку
  • Предложение использовать AUTO_INCREMENT - это синтаксис MySQL, в PostgreSQL его нет

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью