Мало времени до собеседования Golang Что учить и делать
Сегодня мы разберём подробную инструкцию по подготовке к собеседованию на позицию 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 Read | Non-Repeatable Read | Phantom Read |
|---|---|---|---|
| Read Uncommitted | Возможна | Возможна | Возможна |
| Read Committed | Нет | Возможна | Возможна |
| Repeatable Read | Нет | Нет | Возможна* |
| Serializable | Нет | Нет | Нет |
*В PostgreSQL Repeatable Read также защищает от phantom read через механизм SSI.
Диагрессия медленной БД — от простого к сложному:
- Проверить индексы (EXPLAIN / EXPLAIN ANALYZE)
- Оптимизировать запросы (избегать SELECT *, N+1 проблема)
- Настроить параметры СУБД (shared_buffers, work_mem и т.д.)
- Заменить HDD на SSD
- Партиционирование таблиц (по дате, по диапазону)
- Репликация (read replicas для распределения нагрузки чтения)
- Шардирование (горизонтальное разделение данных между серверами)
- Кэширование (Redis, Memcached)
- Денормализация схемы
- Переход на специализированное хранилище (колоночное, временные ряды и т.д.)
Дополнительно стоит повторить:
- Интерфейсы в 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 дней ключевое — не пытаться охватить всё, а углубиться в то, что чаще всего спрашивают, и уметь рассуждать вслух, даже если точный ответ неизвестен.
