Kafka: почему она настолько быстрая — Часть 5

Содержание
Коротко: Kafka быстрая не из-за магии, а из-за простых инженерных решений: последовательной записи, Page Cache, batching, partitioning, неизменяемости и Zero Copy.

Когда разработчики впервые слышат, что Kafka способна обрабатывать миллионы сообщений в секунду, возникает закономерный вопрос:

Как обычный брокер сообщений может работать настолько быстро?

Кажется, что внутри должны быть какие-то сложные алгоритмы, хитрые структуры данных или хранение всего в памяти. Желательно ещё немного магии и отдельный процесс, который шепчет диску «давай быстрее».

На самом деле всё наоборот.

Главный секрет Kafka — простота.

Большинство решений в её архитектуре направлено не на усложнение, а на устранение лишних операций. Kafka не пытается победить физику. Она просто старается не мешать ей работать.

Миф №1. Kafka хранит всё в памяти

Очень распространённое заблуждение.

На самом деле Kafka хранит сообщения на диске.

Именно поэтому она может:

  • хранить терабайты данных;
  • переживать перезапуск сервера;
  • позволять перечитывать историю сообщений.

Но возникает следующий вопрос.

Если Kafka постоянно пишет на диск, почему она быстрее многих систем, работающих в памяти?

Ответ кроется в способе записи данных.

Последовательная запись вместо случайной

Представьте библиотекаря.

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

Положить книгу на полку 10.

Потом:

На полку 5000.

Потом:

На полку 213.

Потом:

На полку 8971.

Каждый раз приходится бегать по библиотеке. Через пару минут библиотекарь начинает мечтать о другой профессии.

Это аналог Random Write.

Именно так работают многие базы данных при обновлении существующих записей.

Kafka делает иначе.

Она всегда кладёт новую книгу в конец полки.

Книга 1

Книга 2

Книга 3

Книга 4

   |
   v

Новая книга

Никаких поисков.

Никаких перемещений.

Никаких обновлений.

Только добавление.

Такой способ называется Sequential Write.

Почему последовательная запись быстрее

Современные накопители оптимизированы именно под последовательный поток данных.

Для HDD это особенно важно.

При случайной записи головке диска приходится постоянно перемещаться между секторами.

10
 |
 v
5000
 |
 v
200
 |
 v
7000
 |
 v
15

Это занимает миллисекунды.

А миллисекунды для диска — очень долго.

При последовательной записи головка практически не двигается.

10
11
12
13
14
15

Диск просто непрерывно записывает поток данных.

Даже современные SSD работают эффективнее при последовательной записи благодаря внутренним алгоритмам управления памятью.

Поэтому Kafka не пытается быть базой данных.

Она специально отказалась от операций:

  • UPDATE;
  • DELETE;
  • случайной вставки.

Есть только одна операция:

Append.

Page Cache — самый недооценённый механизм Kafka

Когда говорят о производительности Kafka, многие вспоминают партиции или батчинг.

Но одна из главных причин её скорости — Page Cache операционной системы.

И это важно понимать.

Что такое Page Cache

Когда приложение записывает файл, данные далеко не всегда сразу попадают на диск.

Обычно происходит следующее:

Kafka
   |
   v
Kernel
   |
   v
Page Cache (RAM)
   |
   v
Disk

Операционная система сначала помещает данные в оперативную память.

И только потом, в удобный момент, записывает их на диск.

Для Kafka это означает две вещи:

  • запись завершается быстрее;
  • операционная система сама оптимизирует работу с диском.

Kafka сознательно не реализует собственный кэш — она доверяет эту задачу ядру ОС.

Почему это эффективно

Представьте, что Consumer запрашивает сообщение, которое было записано несколько секунд назад.

Высока вероятность, что оно всё ещё находится в Page Cache.

Тогда чтение происходит не с диска, а напрямую из оперативной памяти.

Consumer
   |
   v
RAM

В результате скорость оказывается сопоставимой с работой in-memory систем, хотя данные формально хранятся на диске.

Batching — меньше сетевых запросов

Каждый сетевой запрос имеет свою стоимость.

Представим, что Producer отправляет тысячу сообщений.

Первый вариант:

1000 сообщений
   |
   v
1000 запросов

Каждый запрос требует:

  • открытия соединения;
  • передачи заголовков;
  • обработки на Broker.

Это дорого.

Kafka поступает иначе.

Она объединяет сообщения.

1000 сообщений
   |
   v
1 Batch
   |
   v
1 запрос

Получается значительно меньше накладных расходов.

И Producer, и Broker тратят меньше процессорного времени на обслуживание сети.

Параллелизм через Partition

Мы уже знаем, что Topic разбивается на несколько Partition.

Теперь становится понятно, почему это важно с точки зрения производительности.

Представим одну Partition.

Producer
   |
   v
Partition 0

Все сообщения проходят через один журнал.

Добавим ещё три Partition.

Producer
   |
   v

P0

P1

P2

P3

Теперь четыре независимых потока могут:

  • записываться параллельно;
  • читаться параллельно;
  • обслуживаться разными Broker.

Получается почти линейное масштабирование по мере роста числа партиций, если нагрузка равномерно распределена по ключам. Ключевое слово здесь «если»: горячий ключ способен испортить настроение даже очень бодрому кластеру.

Почему Kafka не тратит время на изменение сообщений

Во многих системах сообщение может изменяться после записи.

Kafka этого не делает.

После того как Record записан:

Offset 12345
   |
   v
Record

Он становится неизменяемым.

Это даёт сразу несколько преимуществ:

  • не нужны блокировки на изменение данных;
  • проще репликация;
  • легче восстанавливаться после сбоев;
  • журнал остаётся согласованным.

Неизменяемость (immutability) — одна из ключевых идей Kafka.

Ещё одна оптимизация: Zero Copy

Хотя в исходной статье эта тема не рассматривается, стоит упомянуть её для полноты картины.

При передаче данных Consumer Kafka может использовать системный вызов sendfile(). Это позволяет отправлять данные из файлового кэша операционной системы напрямую в сетевой сокет, минуя лишние копирования между пространством ядра и пользовательским процессом.

В результате:

  • снижается нагрузка на CPU;
  • уменьшается количество копирований памяти;
  • возрастает скорость передачи больших объёмов данных.

Именно сочетание append-only log, Page Cache, batching, partitioning и Zero Copy делает Kafka одной из самых производительных платформ потоковой обработки данных.

Примечание: механизм Zero Copy не описан в исходном материале и добавлен здесь как дополнительное пояснение.

Что в итоге делает Kafka такой быстрой?

Можно выделить несколько основных причин:

ПричинаПочему это ускоряет работу
Append-only logТолько последовательная запись
Sequential I/OМаксимальная эффективность дисковой подсистемы
Page CacheБольшинство чтений выполняется из RAM
BatchingМеньше сетевых запросов и накладных расходов
PartitioningПараллельная запись и чтение
ImmutabilityНет затрат на обновление данных
Zero CopyМинимум копирования данных при передаче

Каждый из этих механизмов сам по себе даёт выигрыш. Вместе они позволяют Kafka достигать очень высокой пропускной способности даже на обычном серверном оборудовании.

Что дальше?

Теперь мы понимаем:

  • почему Kafka использует append-only log;
  • как Page Cache ускоряет чтение и запись;
  • зачем нужны batching и partitioning;
  • почему неизменяемость данных упрощает масштабирование.

Но остаётся ещё один важный вопрос.

Kafka — это очередь сообщений или платформа потоковой обработки?

Ответ не так очевиден, как кажется. В следующей части разберём различия между Message Queue и Streaming Platform, выясним, почему Kafka умеет быть и тем и другим, и почему из-за этого её иногда используют там, где хватило бы обычной очереди и спокойной жизни.