> Как работает масштабирование Kafka: репликация и шардирование (Go)

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

Компании: Ozon

Стек: Go

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

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

Kafka масштабируется горизонтально через шардирование топиков на партиции и репликацию партиций между брокерами. Партиции - единица параллелизма и упорядоченности: внутри партиции порядок гарантирован, между партициями - нет. Репликация обеспечивает отказоустойчивость: каждая партиция имеет leader и follower-реплики на разных брокерах. Продюсеры пишут в leader, консьюмеры читают из leader. При сбое брокера контроллер Kafka выбирает новую leader-реплику из in-sync replicas (ISR).

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

Шардирование (партиционирование)

Топик разбивается на N партиций. Каждая партиция - это упорядоченный лог с offset. Распределение партиций по брокерам кластера - это и есть шардирование. Ключевые следствия:

  • Пропускная способность топика масштабируется добавлением партиций (при условии, что консьюмер-группа имеет достаточно консьюмеров).
  • Порядок сообщений гарантируется только в пределах одной партиции.
  • Выбор партиции определяется ключом сообщения (hash) или round-robin, если ключ null.
  • Количество партиций задаётся при создании топика и может быть увеличено, но уменьшение не поддерживается (это trade-off: увеличение ломает ключевой порядок).

Репликация

Каждая партиция имеет replication.factor реплик (обычно 3). Одна реплика - leader, остальные - followers. Механика:

  • Продюсеры и консьюмеры работают только с leader.
  • Followers реплицируют данные от leader через fetch-запросы.
  • Реплика считается in-sync (ISR), если она не отстала от leader больше, чем на replica.lag.time.max.ms (по умолчанию 30 секунд).
  • При сбое leader контроллер выбирает новую leader из ISR. Если ISR пуст, возможен выбор из out-of-sync реплик при unclean.leader.election.enable=true - это trade-off между доступностью и консистентностью.

Взаимодействие шардирования и репликации

Каждая партиция - это отдельная единица репликации. Например, топик с 10 партициями и replication.factor=3 создаёт 30 реплик, распределённых по брокерам. Kafka-администратор может управлять распределением через kafka-reassign-partitions.sh.

Роль контроллера

Один из брокеров - контроллер. Он отвечает за:

  • выбор leader-реплик при сбоях
  • обработку изменений в составе кластера
  • управление репликацией при добавлении/удалении брокеров

На практике

В Go-проектах с Kafka (через segmentio/kafka-go или confluent-kafka-go) важно учитывать:

  • Партиционирование на стороне продюсера: если нужен строгий порядок по ключу (например, по user_id), ключ должен быть стабильным. При увеличении числа партиций порядок по ключу ломается.
  • Консьюмер-группы: количество консьюмеров в группе не должно превышать число партиций - лишние будут простаивать.
  • Идемпотентный продюсер и acks=all для гарантий доставки - но это снижает throughput.
  • Мониторинг: следить за UnderReplicatedPartitions и IsrShrinksPerSec - это индикаторы проблем с репликацией.

Пример кода

GO
// Продюсер с ключом для шардирования
package main
import (
"context"
"fmt"
"time"
"github.com/segmentio/kafka-go"
)
func main() {
writer := kafka.Writer{
Addr: kafka.TCP("localhost:9092"),
Topic: "orders",
Balancer: &kafka.Hash{}, // шардирование по ключу
RequiredAcks: kafka.RequireAll, // acks=all
Async: false,
}
defer writer.Close()
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Ключ определяет партицию - гарантия порядка для user_id
err := writer.WriteMessages(ctx, kafka.Message{
Key: []byte("user_123"),
Value: []byte(`{"order_id": 42}`),
})
if err != nil {
fmt.Printf("write failed: %v\n", err)
return
}
fmt.Println("message written to partition by hash of key")
}
GO
// Консьюмер с обработкой партиций
package main
import (
"context"
"fmt"
"log"
"github.com/segmentio/kafka-go"
)
func main() {
reader := kafka.NewReader(kafka.ReaderConfig{
Brokers: []string{"localhost:9092"},
Topic: "orders",
GroupID: "order-processor",
Partition: 0, // можно читать конкретную партицию
MinBytes: 10e3,
MaxBytes: 10e6,
})
defer reader.Close()
ctx := context.Background()
for {
msg, err := reader.ReadMessage(ctx)
if err != nil {
log.Printf("read error: %v", err)
continue
}
fmt.Printf("partition=%d offset=%d key=%s value=%s\n",
msg.Partition, msg.Offset, string(msg.Key), string(msg.Value))
}
}

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

Начните с определения двух осей масштабирования: партиционирование - для параллелизма, репликация - для надёжности. Объясните, что партиция - атомарная единица и упорядоченности, и репликации. Упомяните ISR и роль контроллера. Если спросят про trade-off - говорите про unclean.leader.election и увеличение числа партиций. Для senior-уровня добавьте детали про выбор leader, min.insync.replicas и влияние на гарантии доставки. Не углубляйтесь в реализацию лога на диске, если не спрашивают.

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

  • Понимание, что партиции - это шардирование, а реплики - отказоустойчивость.
  • Знание механики ISR и последствий для консистентности.
  • Умение объяснить, почему порядок гарантирован только внутри партиции.
  • Понимание влияния числа партиций на throughput и на операции с ключами.
  • Знание, как Kafka восстанавливается после сбоя брокера.

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

  • Путать партиции и реплики: говорить, что реплики - это копии топика целиком.
  • Утверждать, что Kafka гарантирует глобальный порядок сообщений.
  • Не упоминать ISR при разговоре о выборе leader.
  • Считать, что увеличение партиций всегда улучшает производительность - на самом деле это увеличивает число файлов и накладные расходы на репликацию.
  • Забывать, что консьюмеров в группе не должно быть больше партиций.
  • Не учитывать, что изменение числа партиций ломает порядок по ключу.

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

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