Перейти к основному содержимому

Мало времени до собеседования Golang Что учить и делать

· 5 мин. чтения

Сегодня мы разберём подробную инструкцию по подготовке к собеседованию на позицию Go-разработчика, где автор делится конкретным списком тем, которые с высокой вероятностью будут спрашивать на интервью в крупных компаниях типа Ozon и EPAM. В центре внимания — устройство Go runtime (планировщик, garbage collector, мапы, слайсы, конкурентность), а также теория баз данных (индексы, транзакции, ACID, уровни изоляции и оптимизация производительности).

Вопрос 1. Стратегия подготовки к собеседованию на Go-разработчика за 5 дней: приоритеты и ключевые темы.

Таймкод: 00:00:02

Ответ собеседника: Правильный. Рекомендация — в первую очередь решить ключевую задачу по алгоритмам (заранее опубликованную), а затем сосредоточиться на теории: планировщик Go (горутины, состояния, случайный выбор планировщиком), Garbage Collector (алгоритм Mark and Sweep), внутреннее устройство map (хеш-таблица, O(1) поиск, бакеты, пустая структура для set, потокобезопасность — sync.Map и map с мьютексами), устройство слайсов (указатель, length, capacity, рост через append), пакет sync (мьютексы, каналы, WaitGroup). Типы данных (int32, int64 и т.д.) спрашивают крайне редко. Также нужно знать базы данных: B-дерево и хеш-индексы, ACID, уровни изоляции транзакций (dirty read, repeatable read, serializable и др.), аномалии, которые они решают, локи. При вопросе «база данных медленно работает — что делать?» — отвечать от простого к сложному: проверить индексы, заменить HDD на SSD, использовать виртуальные таблицы (партиционирование), применить шардирование и далее — всего нужно знать около 7 способов. Интервьюер может намекать, чтобы кандидат нашёл нужный способ и обсудил его.

Правильный ответ:

Ответ кандидата достаточно полный и структурированный. Дополню несколькими важными аспектами, которые стоит учесть при подготовке в сжатые сроки.

День 1–2: Алгоритмы и ключевая задача

Если компания заранее опубликовала задачу — это приоритет номер один. Решите её несколько раз, оптимизируйте, подготовьте объяснение сложности по времени и памяти. Параллельно повторите основные структуры данных: слайсы, map, связные списки, стеки, очереди, деревья. На собеседовании часто просят оценить сложность операций.

День 3: Ядро Go — конкурентность и планировщик

Планировщик Go (GMP-модель) — одна из самых частых тем. Нужно понимать:

  • G (goroutine) — легковесный поток, управляемый рантаймом, а не ОС
  • M (machine) — реальный поток ОС
  • P (processor) — логический процессор, связывающий G и M

Горутины проходят через состояния: runnable, running, waiting. Планировщик использует work-stealing для балансировки нагрузки между P. Системные вызовы блокируют M, но не P — рантайм создаёт новый M или переиспользует существующий.

// Пример: горутина блокируется на канале, планировщик переключается
ch := make(chan int)
go func() {
ch <- 42 // блокировка, если нет читателя
}()
val := <-ch

Каналы — не просто очереди. Нужно знать разницу между буферизированными и небуферизированными, поведение при закрытии (close), паттерны fan-out/fan-in, worker pool.

// Worker pool с ограничением конкурентности
func workerPool(jobs <-chan int, results chan<- int, workers int) {
var wg sync.WaitGroup
for i := 0; i < workers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for job := range jobs {
results <- process(job)
}
}()
}
wg.Wait()
close(results)
}

День 4: Внутренние структуры и память

Map — хеш-таблица с бакетами (bucket). Каждый бакет содержит до 8 пар ключ-значение. При переполнении создаётся overflow bucket. Поиск O(1) в среднем, O(n) в худшем случае. Map не потокобезопасен — для конкурентного доступа используется sync.Map (оптимизирован для сценариев с частым чтением и редкой записью) или map + sync.RWMutex.

// Потокобезопасный map с RWMutex
type SafeMap struct {
mu sync.RWMutex
m map[string]int
}

func (s *SafeMap) Get(key string) (int, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
val, ok := s.m[key]
return val, ok
}

Слайс — структура из трёх полей: указатель на базовый массив, length, capacity. append при нехватке capacity создаёт новый массив с увеличенным размером (обычно ×2 для малых слайсов, ×1.25 для больших). Важно понимать, что несколько слайсов могут указывать на один базовый массив — это источник багов.

a := make([]int, 3, 5) // len=3, cap=5
b := a[:4] // OK, оба указывают на один массив
a = append(a, 1) // может переаллоцировать, если cap исчерпан

Garbage Collector — трицветный алгоритм Mark and Sweep (с Go 1.5+ — конкурентный, с низкой паузой STW). Объекты белые (кандидаты на удаление), серые (достижимы, но не отсканированы), чёрные (достижимы и отсканированы). Фаза mark конкурентна с приложением, фаза sweep тоже. Настройка через GOGC (процент роста кучи перед запуском GC).

sync пакет — помимо Mutex, RWMutex, WaitGroup, стоит знать:

  • sync.Once — гарантия однократного выполнения
  • sync.Pool — пул объектов для уменьшения аллокаций (объекты могут быть собраны GC в любой момент)
  • sync/atomic — атомарные операции для lock-free структур

День 5: Базы данных и диагностика производительности

Индексы: B-дерево (сбалансированное, все листья на одном уровне, поиск O(log n)) — основной тип индекса в PostgreSQL, MySQL. Хеш-индексы — O(1) для точечного поиска, но не поддерживают range-запросы.

ACID: Atomicity (всё или ничего), Consistency (переход между согласованными состояниями), Isolation (параллельные транзакции не мешают друг другу), Durability (после commit данные не теряются).

Уровни изоляции (от слабого к строгому):

УровеньDirty ReadNon-Repeatable ReadPhantom Read
Read UncommittedВозможнаВозможнаВозможна
Read CommittedНетВозможнаВозможна
Repeatable ReadНетНетВозможна*
SerializableНетНетНет

*В PostgreSQL Repeatable Read также защищает от phantom read через механизм SSI.

Диагрессия медленной БД — от простого к сложному:

  1. Проверить индексы (EXPLAIN / EXPLAIN ANALYZE)
  2. Оптимизировать запросы (избегать SELECT *, N+1 проблема)
  3. Настроить параметры СУБД (shared_buffers, work_mem и т.д.)
  4. Заменить HDD на SSD
  5. Партиционирование таблиц (по дате, по диапазону)
  6. Репликация (read replicas для распределения нагрузки чтения)
  7. Шардирование (горизонтальное разделение данных между серверами)
  8. Кэширование (Redis, Memcached)
  9. Денормализация схемы
  10. Переход на специализированное хранилище (колоночное, временные ряды и т.д.)

Дополнительно стоит повторить:

  • Интерфейсы в Go (пустой интерфейс interface{} / any, type assertion, type switch)
  • Обработка ошибок (errors.Is, errors.As, fmt.Errorf с %w для wrapping)
  • Context — отмена, таймауты, передача значений
  • Базовые паттерны: graceful shutdown, circuit breaker, retry с exponential backoff
  • HTTP в Go: net/http, маршрутизация, middleware

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