Learning Platform
Глоссарий Troubleshooting
Урок 11.03 · 35 мин
Продвинутый
Performance TuningThroughputLatencybatch.sizelinger.msacksCompressionfetch.min.bytes

Performance Tuning с числовыми целями

Самый распространённый совет по оптимизации Kafka: “увеличьте batch.size”. Это бесполезно без конкретных цифр. Этот урок устроен иначе: каждая рекомендация сопровождается конкретным значением, конкретной целью и конкретным trade-off.

Два базовых режима: throughput-optimized и latency-optimized. Они конфликтуют — нельзя максимизировать оба одновременно. Выбор определяется use case.


Два режима: throughput vs latency

Throughput vs Latency: фундаментальный trade-off
Увеличение batch.size и linger.ms повышает throughput, но добавляет latency. Уменьшение обеих ускоряет доставку, но снижает эффективность.
Throughput-оптимизированныйЦель: максимальная пропускная способность. batch.size=1MB, linger.ms=50. Потребители: аналитика, ETL, batch-обработка. Допустимая задержка: 50-200ms. Достижимо: 100K+ records/s per producer.
vs
Latency-оптимизированныйЦель: минимальная задержка доставки. batch.size=16KB, linger.ms=0. Потребители: real-time мониторинг, алерты, финтех. Целевая задержка: менее 5ms p99. Throughput ограничен: 20-50K records/s.
Аналитика / ETL / LogsТипичные use cases для throughput-режима: сбор логов в ELK/ClickHouse, ETL-пайплайны, аналитические события. Данные можно буферизировать -- задержка в 50ms не критична.
приоритет
Мониторинг / Fraud detection / TradingТипичные use cases для latency-режима: системы обнаружения мошенничества, алгоритмический трейдинг, real-time метрики для автоматических реакций. Каждая миллисекунда задержки имеет значение.

Producer throughput optimization: конкретные числа

Baseline конфигурация для throughput-оптимизированного producer:

# Throughput-optimized producer
batch.size=1048576              # 1 MB (default: 16384 = 16 KB)
linger.ms=50                    # ждать 50ms для заполнения batch (default: 0)
compression.type=lz4            # быстрое сжатие (default: none)
buffer.memory=67108864          # 64 MB буфер (default: 33554432 = 32 MB)
max.in.flight.requests.per.connection=5   # параллельных запросов (default: 5)
acks=all                        # максимальная durability

Цель: 100K+ records/s при avg msg size 500 bytes = 50 MB/s

Путь записи через producer accumulator
Batch заполняется до batch.size или истекает linger.ms — что наступит раньше

producer.send()

producer.send() -- запись поступает в RecordAccumulator. Данный момент: начало отсчёта linger.ms.
RecordAccumulator (buffer)Буфер для накопления записей перед отправкой. Размер контролируется buffer.memory (64 MB). При заполнении буфера producer блокируется (max.block.ms). batch-size-avg JMX-метрика показывает средний заполненный размер -- сравнивайте с batch.size.
Batch готов? batch.size OR linger.msBatch отправляется когда: (1) размер достиг batch.size=1MB, ИЛИ (2) истекло linger.ms=50ms. Что наступит раньше. При 1000 records/s по 500 bytes: за 50ms накопится 25KB -- намного меньше batch.size. Batch отправится по linger.ms. При 100K records/s: за 50ms накопится 2.5MB -- batch достигнет 1MB раньше и отправится раньше linger.ms.
Сжатие (lz4)При compression.type=lz4: batch сжимается перед отправкой. lz4 даёт 2-2.5x сжатие при скорости ~500 MB/s. Для batch 1MB: сжатый размер ~400-500KB. Снижает сетевой трафик и объём данных на диске брокера.

Брокер (ACK)

Сжатый batch отправляется брокеру. max.in.flight.requests.per.connection=5 позволяет держать 5 незавершённых batch в полёте -- конвейеризация повышает throughput. ACK получен -- batch из буфера удаляется.

Почему batch.size=1MB, а не 16KB (default)?

При 16KB batch и 100K records/s по 500 bytes:

  • Throughput в байтах: 100,000 * 500 = 50 MB/s
  • Размер batch: 16 KB
  • Количество запросов к брокеру: 50 MB / 16 KB = ~3,200 запросов/с
  • Каждый запрос: round trip к брокеру (~1-5ms), CPU overhead на обе стороны

При 1MB batch и той же нагрузке:

  • Количество запросов к брокеру: 50 MB / 1 MB = 50 запросов/с
  • В 64 раза меньше запросов — соответственно ниже overhead

Наблюдаемые результаты:

  • batch.size=1MB + linger.ms=50 + compression=lz4: 200K-500K records/s (зависит от msg size)
  • batch.size=16KB + linger.ms=0: 20K-50K records/s

Producer latency optimization: конкретные числа

# Latency-optimized producer
acks=1                          # только лидер-ACK (не ждём ISR)
batch.size=16384                # 16 KB (default) — меньше ждать заполнения
linger.ms=0                    # отправлять немедленно, не ждать
compression.type=none           # нет сжатия = нет CPU-задержки
buffer.memory=33554432          # 32 MB (default) достаточно
max.block.ms=5000               # 5 сек блокировки при полном буфере

Цель: менее 5ms p99 produce latency (в пределах одного датацентра)

Сравнение: throughput vs latency конфигурации
ПараметрПараметр конфигурации
ThroughputThroughput-оптимизированная конфигурация
LatencyLatency-оптимизированная конфигурация
batch.sizeРазмер batch до принудительной отправки. Больше = выше throughput. Меньше = быстрее отправка.
1048576 (1MB)1 MB: много записей в одном запросе. Batch заполняется за 50ms при высокой нагрузке.
16384 (16KB)16 KB: default значение. Batch заполняется быстро или отправляется сразу (linger.ms=0).
linger.msВремя ожидания для заполнения batch. 0 = отправлять немедленно.
50msЖдать 50ms для заполнения batch. Добавляет задержку, но увеличивает эффективность batch-ing.
0msНе ждать. Отправить немедленно после producer.send(). Минимальная задержка.
compression.typeАлгоритм сжатия. none = нет overhead на CPU. lz4 = быстрое сжатие.
lz4lz4: ~2x compression ratio, ~500 MB/s скорость. Снижает сетевой трафик.
noneНет сжатия. Нет CPU overhead на сериализацию/десериализацию. Минимальная задержка.
acksПодтверждение записи. all = ISR подтвердила. 1 = только лидер.
allЖдать подтверждения всех ISR-реплик. Максимальная durability. +50-100ms latency при acks=all.
1Только лидер. Риск: если лидер упадёт до репликации -- данные потеряны. Минимальная задержка.
Достижимый throughputРеальный throughput при средней записи 500 bytes.
100K-500K records/sПри среднем размере записи 500 bytes и batch.size=1MB: 100K+ records/s per producer.
20K-50K records/sПри latency-режиме и немедленной отправке: количество запросов ограничивает throughput.
Целевая latency p99Задержка на 99-м перцентиле.
50-150msМинимум linger.ms=50ms плюс обработка брокером. При acks=all и ISR-ожидании может быть выше.
меньше 5msПри acks=1 и linger.ms=0 в пределах одного датацентра: 1-5ms p99 достижимо.

Consumer throughput optimization: конкретные числа

# Throughput-optimized consumer
fetch.min.bytes=1048576         # ждать 1MB данных перед возвратом fetch (default: 1)
fetch.max.wait.ms=500           # максимум 500ms ждать fetch.min.bytes (default: 500)
max.poll.records=1000           # возвращать до 1000 записей за poll() (default: 500)
fetch.max.bytes=52428800        # максимум 50MB в ответе на fetch (default: 52428800)
max.partition.fetch.bytes=1048576   # 1MB per partition per fetch (default: 1048576)

Цель: consumer удерживает lag менее 1000 записей при input rate = 50K records/s

Почему fetch.min.bytes=1MB важно?

При fetch.min.bytes=1 (default): брокер отвечает на каждый fetch-запрос немедленно, даже если данных нет. При интенсивном потреблении это создаёт тысячи пустых fetch-запросов в секунду.

При fetch.min.bytes=1MB: брокер ждёт накопления 1MB данных (или истечения fetch.max.wait.ms=500ms). Количество fetch-запросов резко падает, снижается overhead на обе стороны.

Consumer lag: уровни и действия
records-lag-max: от нормы до критического инцидента
Lag меньше 100НОРМАЛЬНО. Consumer успевает за продюсерами. Мониторьте, но действий не требуется.
Lag 100-1000НАБЛЮДЕНИЕ. Lag накапливается. Причины: временный всплеск нагрузки, медленная обработка. Действие: мониторить тренд. Если lag растёт -- переходим в следующую зону.
Lag 1000-10000WARNING. Серьёзное отставание. Действия: (1) Scale consumer group: добавить инстансы (до числа партиций). (2) Проверить max.poll.interval.ms: consumer исключается из группы если не вызывает poll() за это время. (3) Проверить обработку: не блокируется ли consumer внешними вызовами.
Lag больше 10000КРИТИЧНО. Немедленные действия: (1) Scale consumers (горизонтально, до max=partitions). (2) Проверить: нет ли rebalancing storm (frequent rebalances prevent consumption). (3) Проверить stuck processing (thread dump). (4) Рассмотреть увеличение retention если lag продолжает расти -- данные могут быть удалены до обработки.

Масштабирование consumer group:

Максимальный параллелизм = число партиций топика. Нельзя назначить двух consumers на одну партицию в рамках одной группы.

# Пример: топик orders, 12 партиций
# Consumer group с 6 инстансами: каждый обрабатывает 2 партиции
# Consumer group с 12 инстансами: каждый обрабатывает 1 партицию (максимальный параллелизм)
# Consumer group с 13 инстансами: один инстанс будет idle (лишний)

Broker-side tuning: параметры I/O

# Broker thread configuration
num.io.threads=8               # I/O потоки (default: 8, увеличить если RequestHandlerAvgIdlePercent < 0.3)
num.network.threads=3          # сетевые потоки (default: 3, увеличить при высоком connection count)
num.replica.fetchers=1         # потоки для репликации от фолловеров (default: 1, увеличить если follower lag растёт)

# Socket buffers
socket.send.buffer.bytes=102400    # TCP send buffer (100 KB)
socket.receive.buffer.bytes=102400 # TCP receive buffer (100 KB)
socket.request.max.bytes=104857600 # Максимальный размер запроса (100 MB)

Когда увеличивать num.io.threads:

RequestHandlerAvgIdlePercent ниже 0.30 в течение 10 минут → попробовать увеличить num.io.threads с 8 до 12 или 16. Каждый I/O поток обрабатывает сетевые запросы: Produce, Fetch, Metadata. При высокой нагрузке очередь заполняется.

Когда увеличивать num.replica.fetchers:

kafka.server:type=ReplicaFetcherManager,name=MaxLag растёт → фолловеры не успевают реплицировать. Увеличение num.replica.fetchers с 1 до 2-4 позволяет нескольким потокам параллельно реплицировать от разных лидеров.


Сравнение алгоритмов сжатия: числа

Алгоритмы сжатия Kafka: сравнение
АлгоритмАлгоритм сжатия
RatioКоэффициент сжатия (во сколько раз уменьшается размер)
Сжатие MB/sСкорость сжатия (на стороне producer)
Дек. MB/sСкорость декомпрессии (на стороне consumer/broker)
CPUНагрузка на CPU (LOW/MEDIUM/HIGH)
РекомендацияРекомендованный сценарий использования
noneБез сжатия. Нет CPU overhead. Максимальная throughput с минимальной latency. Использует максимальную пропускную способность сети.
1.0xНет сжатия -- данные занимают столько же места, сколько и до.
N/AНет операции сжатия.
N/AНет операции декомпрессии.
0%Никакой нагрузки на CPU для сжатия/декомпрессии.
Latency-критичные системыИспользовать при: latency меньше 5ms p99, сеть не является bottleneck, данные уже сжаты (бинарные форматы).
lz4Рекомендованный алгоритм по умолчанию для throughput. Быстрый, хорошее сжатие.
2.0-2.5xТипичное сжатие текстовых/JSON данных. 1MB сырых данных -> ~400-500KB.
~500 MB/sОчень высокая скорость сжатия. Producer не является bottleneck при использовании lz4.
~800 MB/sДекомпрессия ещё быстрее сжатия. Consumer overhead минимален.
LOWМинимальная нагрузка на CPU. Подходит для любых серверов.
Default для throughputИспользовать как baseline для всех throughput-оптимизированных систем. Замените snappy на lz4.
snappyLegacy алгоритм. Был popular в Hadoop-экосистеме. Медленнее lz4, хуже ratio. Не рекомендуется для новых систем.
1.5-2.0xХуже чем lz4 по ratio. Для большинства форматов данных.
~400 MB/sМедленнее lz4. Нет преимуществ перед lz4.
~600 MB/sМедленнее lz4 по декомпрессии.
LOWНизкая нагрузка на CPU, но хуже чем lz4 по throughput.
Legacy (использовать lz4)Если используете snappy -- замените на lz4. Лучше по всем параметрам.
zstdZstandard: лучший ratio, но больше CPU. Рекомендован при ограниченной пропускной способности сети.
2.5-3.5xЛучший коэффициент сжатия. 1MB -> ~300-400KB. Значительная экономия на сети и диске.
~200 MB/sМедленнее lz4 по скорости сжатия. При высокой нагрузке producer может стать bottleneck на CPU.
~500 MB/sПриемлемая скорость декомпрессии на стороне consumer.
MEDIUMЗаметная нагрузка на CPU по сравнению с lz4. Требует оценки CPU capacity.
Bandwidth-limited сетиИспользовать когда: сеть является bottleneck (1 Gbps NIC при высокой нагрузке), данные хранятся долго (retention 30+ дней), CPU не является ограничением.
gzipLegacy алгоритм. Медленный. Не рекомендован для Kafka.
2.0-3.0xХороший ratio, но медленная скорость делает его неэффективным.
~50 MB/sВ 10 раз медленнее lz4. Серьёзный bottleneck при высокой нагрузке.
~200 MB/sМедленная декомпрессия накапливает задержки на стороне consumer.
HIGHВысокая нагрузка на CPU. В высоконагруженных системах может стать bottleneck.
ИзбегатьGzip -- legacy выбор. Нет сценариев, где gzip лучше zstd или lz4. Не рекомендован.

End-to-end latency: анатомия задержки

End-to-end latency: от producer.send() до consumer.poll()
Каждый этап добавляет задержку. Hover для типичных значений.
Producer batch (linger.ms)Первый этап: запись ждёт в RecordAccumulator. При linger.ms=50: до 50ms задержки. При linger.ms=0: почти 0. Контролируется конфигурацией producer.
Network (producer to broker)Сетевая задержка от producer к брокеру-лидеру. В пределах датацентра: 0.1-1ms. Между датацентрами: 5-50ms. Выбор датацентра и network topology влияют на этот этап.
Broker processingОбработка на брокере: запись в page cache, обновление индексов. Обычно: 0.5-5ms. При disk bottleneck или высокой нагрузке: до 50ms+ (LocalTimeMs JMX-метрика).
ISR replication (acks=all)Ожидание репликации ISR-фолловерами. Только при acks=all. Включает network latency broker-to-broker + disk write фолловера. Обычно: 1-20ms. (RemoteTimeMs JMX-метрика).
Consumer fetch (fetch.max.wait.ms)Consumer ожидает данных на брокере: до fetch.max.wait.ms=500ms. При активном потоке данных: практически 0 (данные уже есть). При редких данных: полный fetch.max.wait.ms.

consumer.poll()

consumer.poll() вернул запись. Суммарная latency: при linger.ms=0, acks=1, активном потоке данных = 1-5ms p99. При linger.ms=50, acks=all = 60-150ms p99.

Реальные числа end-to-end latency (в пределах одного датацентра):

Конфигурацияp50p99Сценарий
linger.ms=0, acks=11ms5msLatency-критичные системы
linger.ms=0, acks=all, RF=33ms20msДюрабельность + низкая задержка
linger.ms=50, acks=all, RF=355ms100msThroughput-оптимизированный
linger.ms=50, acks=all, сжатие lz457ms110msThroughput + сжатие
WARNING

log.flush.interval.messages и log.flush.interval.ms — НЕ настраивайте в production. Kafka намеренно использует OS page cache для записи и периодически делает fsync (управляемый операционной системой). Принудительный flush после каждого сообщения (log.flush.interval.messages=1) снижает throughput в 10-100 раз. Durability обеспечивается через replication (acks=all, min.insync.replicas=2), не через fsync. Это фундаментальное архитектурное решение Kafka. Не нарушайте его.

TIP

batch.size=1048576 (1 MB) + linger.ms=50 + compression.type=lz4 — это baseline для throughput-оптимизированного producer. Начните с этих значений и корректируйте на основе JMX метрик: batch-size-avg (заполняется ли batch?) и record-queue-time-avg (сколько записей ждут в буфере?).

Проверка знанийKnowledge check
Producer отправляет 10K msg/s, средний размер 1 KB. JMX-метрика batch-size-avg = 2 KB. Текущий конфиг: batch.size=16384 (16 KB), linger.ms=0. Какие два config-параметра нужно изменить и до каких значений, чтобы увеличить batch fill? Какой trade-off это создаст?
ОтветAnswer
batch-size-avg = 2 KB при batch.size=16 KB означает: batch отправляется немедленно после одной-двух записей, не дожидаясь заполнения. При linger.ms=0 это ожидаемо: каждый вызов producer.send() немедленно создаёт и отправляет batch. Два параметра для изменения: (1) linger.ms: увеличить с 0 до 50ms. За 50ms при 10K msg/s накопится 500 записей по 1KB = 500KB в batch (намного лучше 2KB). (2) batch.size: увеличить с 16KB до 1048576 (1MB) чтобы batch мог принять все накопленные за 50ms записи. Trade-off: добавляет задержку до 50ms. Каждая запись будет ждать в буфере до отправки batch. Если use case требует менее 50ms latency -- эта оптимизация недопустима. После изменений проверить batch-size-avg (должен вырасти к 500KB+) и record-queue-time-avg (ожидаемо вырастет до ~50ms).

Проверьте понимание

Результат: 0 из 0
Аналитический
Вопрос 1 из 4. Throughput-оптимизированный producer настроен: batch.size=1048576, linger.ms=50, compression.type=lz4, acks=all. Текущая производительность: 15K records/s при целевой 100K records/s. JMX batch-size-avg = 950KB, record-queue-time-avg = 50ms. Почему throughput ниже целевой?

Закончили урок?

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

Войдите чтобы оценить урок

Прогресс модуля
0 из 6