Дизайн Kafka, эффективность

При создании Kafka были приложены значительные усилия для повышения эффективности. Один из основных вариантов использования - обработка данных о веб-активности, объем которых очень велик: каждый просмотр страницы может генерировать десятки операций записи. Кроме того каждое опубликованное сообщение читается по крайней мере одним потребителем (часто многими), поэтому стремились сделать потребление как можно более дешевым.

На основе опыта создания и эксплуатации ряда аналогичных систем также обнаружили, что эффективность является ключом к эффективной работе с несколькими арендаторами. Если нижестоящая инфраструктурная услуга может легко стать узким местом из-за небольшого увеличения использования приложением, такие небольшие изменения часто создают проблемы. Работая очень быстро, гарантируется, что приложение перевернется под нагрузкой раньше, чем инфраструктура. Это особенно важно при попытке запустить централизованную службу, которая поддерживает десятки или сотни приложений в централизованном кластере, поскольку изменения в шаблонах использования происходят почти ежедневно.

Эффективность диска обсуждалась в предыдущем посте. После того, как плохие шаблоны доступа к диску были устранены, есть две общие причины неэффективности в этом типе системы: слишком много мелких операций ввода-вывода и чрезмерное копирование байтов.

Небольшая проблема ввода-вывода возникает как между клиентом и сервером, так и в собственных постоянных операциях сервера.

Чтобы избежать этого, протокол построен на абстракции «набор сообщений», которая естественным образом группирует сообщения вместе. Это позволяет сетевым запросам группировать сообщения вместе и амортизировать накладные расходы на обратную передачу по сети, а не отправлять по одному сообщению за раз. Сервер, в свою очередь, за один раз добавляет в свой журнал порции сообщений, а потребитель извлекает большие линейные порции за раз.

Эта простая оптимизация дает ускорение на порядки. Пакетная обработка приводит к более крупным сетевым пакетам, большим последовательным операциям с диском, непрерывным блокам памяти и так далее, и все это позволяет Kafka превращать прерывистый поток случайных записей сообщений в линейные записи, которые поступают к потребителям.

Другая неэффективность заключается в копировании байтов. При низкой скорости передачи сообщений это не проблема, но под нагрузкой влияние оказывается значительным. Чтобы избежать этого, используется стандартизированный двоичный формат сообщения, который используется производителем, брокером и потребителем (поэтому блоки данных могут передаваться между ними без изменений).

Журнал сообщений, поддерживаемый посредником, сам по себе представляет собой просто каталог файлов, каждый из которых заполнен последовательностью наборов сообщений, записанных на диск в том же формате, который используется производителем и потребителем. Поддержание этого общего формата позволяет оптимизировать наиболее важную операцию: передачу по сети постоянных фрагментов журнала. Современные операционные системы Unix предлагают высоко оптимизированный кодовый путь для передачи данных из кэша страниц в сокет; в Linux это делается с помощью системного вызова sendfile.

Чтобы понять влияние sendfile, важно понимать общий путь к данным для передачи данных из файла в сокет:

  1. Операционная система считывает данные с диска в кэш страниц в пространстве ядра.
  2. Приложение считывает данные из пространства ядра в буфер пространства пользователя.
  3. Приложение записывает данные обратно в пространство ядра в буфер сокета.
  4. Операционная система копирует данные из буфера сокета в буфер сетевого адаптера, откуда они отправляются по сети.

Это явно неэффективно, есть четыре копии и два системных вызова. Использование sendfile позволяет избежать этого повторного копирования, позволяя ОС отправлять данные из кэша страниц напрямую в сеть. Таким образом, в этом оптимизированном пути требуется только последняя копия буфера сетевого адаптера.

Ожидается, что обычным вариантом использования будет несколько потребителей по одной теме. Используя приведенную выше оптимизацию с нулевым копированием, данные копируются в кэш страниц ровно один раз и повторно используются при каждом использовании вместо того, чтобы храниться в памяти и копироваться в пространство пользователя каждый раз при чтении. Это позволяет использовать сообщения со скоростью, приближающейся к пределу сетевого подключения.

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

Сквозное пакетное сжатие

В некоторых случаях узким местом является не процессор или диск, а пропускная способность сети. Это особенно верно для конвейера данных, который должен отправлять сообщения между центрами обработки данных по глобальной сети. Конечно, пользователь всегда может сжимать свои сообщения по одному без какой-либо поддержки со стороны Kafka, но это может привести к очень плохим коэффициентам сжатия, поскольку большая часть избыточности возникает из-за повторения сообщений одного типа (например, имена полей в JSON или пользовательские агенты в веб-журналах или общие строковые значения). Эффективное сжатие требует сжатия нескольких сообщений вместе, а не сжатия каждого сообщения по отдельности.

Kafka поддерживает это с помощью эффективного формата пакетной обработки. Пакет сообщений может быть сгруппирован вместе, сжат и отправлен на сервер в этой форме. Этот пакет сообщений будет записан в сжатом виде и останется сжатым в журнале и будет распакован только потребителем.

Kafka поддерживает протоколы сжатия GZIP, Snappy, LZ4 и ZStandard.


Читайте также:

Комментарии

Популярные сообщения из этого блога

Язык поисковых запросов в Graylog

Хэш-таблица: разрешение коллизий

Нормальные формы, пример нормализации в базе данных