Каков основной принцип хранения данных в Apache Kafka?

Ответ

Основа Apache Kafka — это распределённый, отказоустойчивый и неизменяемый журнал фиксации (commit log), который работает по принципу append-only (только дозапись в конец).

Как это устроено:

  1. Топик (Topic):

    • Логическая категория или именованный поток, в который продюсеры публикуют сообщения. Например, user-registrations или order-updates.
  2. Партиция (Partition):

    • Каждый топик разделяется на одну или несколько партиций. Партиция — это и есть физический, упорядоченный и неизменяемый лог-файл.
    • Запись в партицию всегда происходит в конец (append-only).
    • Порядок сообщений гарантируется только в пределах одной партиции.
  3. Офсет (Offset):

    • Каждое сообщение внутри партиции имеет уникальный последовательный идентификатор — офсет (например, 0, 1, 2, ...).
    • Офсет позволяет консьюмерам отслеживать свою позицию чтения и перечитывать сообщения при необходимости.
  4. Репликация (Replication):

    • Для отказоустойчивости каждая партиция реплицируется на несколько брокеров (серверов Kafka). Одна реплика является лидером (принимает запись), остальные — последователями (копируют данные).

Пример записи в Kafka на Go (с использованием segmentio/kafka-go):

package main

import (
    "context"
    "log"
    "github.com/segmentio/kafka-go"
)

func main() {
    w := &kafka.Writer{
        Addr:     kafka.TCP("localhost:9092"),
        Topic:    "my-topic",
        Balancer: &kafka.LeastBytes{},
    }

    err := w.WriteMessages(context.Background(),
        kafka.Message{
            Key:   []byte("Key-A"),
            Value: []byte("Hello Kafka!"),
        },
    )
    if err != nil {
        log.Fatal("failed to write messages:", err)
    }

    if err := w.Close(); err != nil {
        log.Fatal("failed to close writer:", err)
    }
}

Преимущества такого подхода:

  • Высокая производительность: Последовательная запись на диск — одна из самых быстрых дисковых операций.
  • Горизонтальная масштабируемость: Нагрузка распределяется по партициям и брокерам.
  • Отказоустойчивость: Репликация данных защищает от потери сообщений при сбое брокера.
  • Гибкость для потребителей: Консьюмеры могут читать данные со своей скоростью, перечитывать их и работать независимо друг от друга.

Ответ 18+ 🔞

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

Как эта штука устроена, если по-простому:

  1. Топик (Topic):

    • Это типа тематической папки, куда все скидывают свои бумажки. Зарегистрировался новый юзер — кинул записку в папку user-registrations. Сделали заказ — шлёпнул в order-updates. Всё логично, ёпта.
  2. Партиция (Partтиция):

    • А вот тут начинается магия, блядь. Каждую такую папку (топик) можно разорвать на несколько кусков — партиций. Каждый кусок — это физический, тупой как валенок, лог-файл. Новые записи всегда, сука, только в конец прилепляются. Никакого «ой, записал не туда, дай сотру» — нихуя! Порядок сообщений железобетонно гарантирован, но только внутри одного такого куска. Между разными кусками — как повезёт.
  3. Офсет (Offset):

    • Каждой записке в этом куске дают свой порядковый номер: 0, 1, 2, 3... Это и есть офсет, его адрес. Консьюмер (читатель) приходит и говорит: «Я на офсете 42 остановился». И все понимают, что дальше читать с 43. Хочешь перечитать — да пожалуйста, ёбана, двигай указатель назад. Охуенно просто!
  4. Репликация (Replication):

    • Чтобы вся эта конструкция не накрылась медным тазом, если один сервер (брокер) возьмёт и сдохнет, каждый кусок (партицию) копируют на несколько других серверов. Один из них — главный (лидер), он принимает все записи. Остальные — его подпевалы (последователи), тупо копируют за ним. Упал лидер — один из подпевал тут же занимает его место. Хитро, блядь!

Вот тебе пример, как эту хуйню кормить данными на Go:

package main

import (
    "context"
    "log"
    "github.com/segmentio/kafka-go"
)

func main() {
    w := &kafka.Writer{
        Addr:     kafka.TCP("localhost:9092"),
        Topic:    "my-topic",
        Balancer: &kafka.LeastBytes{},
    }

    err := w.WriteMessages(context.Background(),
        kafka.Message{
            Key:   []byte("Key-A"),
            Value: []byte("Hello Kafka!"),
        },
    )
    if err != nil {
        log.Fatal("failed to write messages:", err)
    }

    if err := w.Close(); err != nil {
        log.Fatal("failed to close writer:", err)
    }
}

Видишь? Никакой ебалы-перебалы. Создал писателя, указал куда писать, и впердолил сообщение. Всё, поезд ушёл.

А теперь, сука, почему это всё так охуенно работает:

  • Скорость — пиздец: Писать новые данные всегда в конец файла — это одна из самых быстрых операций для диска. Не надо голову ломать, куда впихнуть.
  • Масштабируется, как сумасшедший: Надоела одна партиция? Разорви топик на десять! Загрузка не лезет на один сервер? Раскидай партиции по кластеру! Горизонтально, блядь.
  • Не убьёшь просто так: Репликация, ёпта. Даже если половина кластера решит сыграть в ящик, данные останутся живы. Отказоустойчивость на уровне паранойи.
  • Читатели — не стадо баранов: Каждый консьюмер может читать в своём темпе, с того места, где остановился. Хочет прочитать старые данные заново — нет проблем, подвинь офсет и вперёд. Полная независимость, в рот меня чих-пых!

Вот и вся философия, если без этих ваших заумных терминов. Просто, надёжно, и работает, как швейцарские часы, только без всей этой их буржуазной мишуры.